mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-15 13:00:25 +02:00
Add visualizer configuration (#1443)
* add visualizer configuration * add visualizer presets * add butterchurn visualizer * wrap visualizers in error boundary
This commit is contained in:
@@ -216,6 +216,120 @@ const PlayerbarSliderSchema = z.object({
|
||||
type: PlayerbarSliderTypeSchema,
|
||||
});
|
||||
|
||||
const AudioMotionAnalyzerSettingsSchema = z.object({
|
||||
alphaBars: z
|
||||
.boolean()
|
||||
.describe(
|
||||
'When set to true each bar’s amplitude affects its opacity, i.e., higher bars are rendered more opaque while shorter bars are more transparent. This is similar to the lumiBars effect, but bars’ amplitudes are preserved and it also works on Discrete mode and radial spectrum.',
|
||||
),
|
||||
ansiBands: z
|
||||
.boolean()
|
||||
.describe(
|
||||
'When set to true, ANSI/IEC preferred frequencies are used to generate the bands for octave bands modes (see mode). The preferred base-10 scale is used to compute the center and bandedge frequencies, as specified in the ANSI S1.11-2004 standard. When false, bands are based on the equal-tempered scale, so that in 1/12 octave bands the center of each band is perfectly tuned to a musical note.',
|
||||
),
|
||||
barSpace: z
|
||||
.number()
|
||||
.describe(
|
||||
'Customize the spacing between bars in frequency bands modes (see mode). Use a value between 0 and 1 for spacing proportional to the band width. Values >= 1 will be considered as a literal number of pixels.',
|
||||
),
|
||||
channelLayout: z
|
||||
.enum(['single', 'dual-combined', 'dual-horizontal', 'dual-vertical'])
|
||||
.describe('Defines the number and layout of analyzer channels.'),
|
||||
colorMode: z
|
||||
.enum(['gradient', 'bar-index', 'bar-level'])
|
||||
.describe('Selects the desired mode for coloring the analyzer bars.'),
|
||||
customGradients: z.array(
|
||||
z.object({
|
||||
colorStops: z.array(
|
||||
z.string().or(
|
||||
z.object({
|
||||
color: z.string(),
|
||||
level: z.number().min(0).max(1).optional(),
|
||||
pos: z.number().min(0).max(1).optional(),
|
||||
}),
|
||||
),
|
||||
),
|
||||
dir: z.string().optional(),
|
||||
name: z.string(),
|
||||
}),
|
||||
),
|
||||
fadePeaks: z
|
||||
.boolean()
|
||||
.describe(
|
||||
'When true, peaks fade out instead of falling down. It has no effect when peakLine is active.',
|
||||
),
|
||||
fftSize: z
|
||||
.number()
|
||||
.describe(
|
||||
'Number of samples used for the FFT performed by the AnalyzerNode. It must be a power of 2 between 32 and 32768, so valid values are: 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, and 32768. Higher values provide more detail in the frequency domain, but less detail in the time domain (slower response), so you may need to adjust smoothing accordingly.',
|
||||
),
|
||||
fillAlpha: z.number(),
|
||||
frequencyScale: z.enum(['bark', 'linear', 'log', 'mel']),
|
||||
gradient: z.string(),
|
||||
gradientLeft: z.string().optional(),
|
||||
gradientRight: z.string().optional(),
|
||||
gravity: z.number(),
|
||||
ledBars: z.boolean(),
|
||||
linearAmplitude: z.boolean(),
|
||||
linearBoost: z.number(),
|
||||
lineWidth: z.number(),
|
||||
loRes: z.boolean(),
|
||||
lumiBars: z.boolean(),
|
||||
maxDecibels: z.number(),
|
||||
maxFPS: z.number(),
|
||||
maxFreq: z.number(),
|
||||
minDecibels: z.number(),
|
||||
minFreq: z.number(),
|
||||
mirror: z.number(),
|
||||
mode: z.number(),
|
||||
noteLabels: z.boolean(),
|
||||
outlineBars: z.boolean(),
|
||||
peakFadeTime: z.number(),
|
||||
peakHoldTime: z.number(),
|
||||
peakLine: z.boolean(),
|
||||
presets: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
value: z.any(),
|
||||
}),
|
||||
),
|
||||
radial: z.boolean(),
|
||||
radialInvert: z.boolean(),
|
||||
radius: z.number(),
|
||||
reflexAlpha: z.number(),
|
||||
reflexBright: z.number(),
|
||||
reflexFit: z.boolean(),
|
||||
reflexRatio: z.number(),
|
||||
roundBars: z.boolean(),
|
||||
showFPS: z.boolean(),
|
||||
showPeaks: z.boolean(),
|
||||
showScaleX: z.boolean(),
|
||||
showScaleY: z.boolean(),
|
||||
smoothing: z.number(),
|
||||
spinSpeed: z.number(),
|
||||
splitGradient: z.boolean(),
|
||||
trueLeds: z.boolean(),
|
||||
volume: z.number(),
|
||||
weightingFilter: z.enum(['', 'A', 'B', 'C', 'D', 'Z']),
|
||||
});
|
||||
|
||||
const ButterchurnSettingsSchema = z.object({
|
||||
blendTime: z.number().min(0).max(10),
|
||||
currentPreset: z.string().optional(),
|
||||
cyclePresets: z.boolean(),
|
||||
cycleTime: z.number().min(1).max(300),
|
||||
includeAllPresets: z.boolean(),
|
||||
maxFPS: z.number().min(0),
|
||||
randomizeNextPreset: z.boolean(),
|
||||
selectedPresets: z.array(z.string()),
|
||||
});
|
||||
|
||||
const VisualizerSettingsSchema = z.object({
|
||||
audiomotionanalyzer: AudioMotionAnalyzerSettingsSchema,
|
||||
butterchurn: ButterchurnSettingsSchema,
|
||||
type: z.enum(['audiomotionanalyzer', 'butterchurn']),
|
||||
});
|
||||
|
||||
export const GeneralSettingsSchema = z.object({
|
||||
accent: z
|
||||
.string()
|
||||
@@ -440,6 +554,7 @@ export const ValidationSettingsStateSchema = z.object({
|
||||
z.literal('window'),
|
||||
z.string(),
|
||||
]),
|
||||
visualizer: VisualizerSettingsSchema,
|
||||
window: WindowSettingsSchema,
|
||||
});
|
||||
|
||||
@@ -1294,6 +1409,70 @@ const initialState: SettingsState = {
|
||||
username: 'feishin',
|
||||
},
|
||||
tab: 'general',
|
||||
visualizer: {
|
||||
audiomotionanalyzer: {
|
||||
alphaBars: false,
|
||||
ansiBands: false,
|
||||
barSpace: 0.1,
|
||||
channelLayout: 'single',
|
||||
colorMode: 'gradient',
|
||||
customGradients: [],
|
||||
fadePeaks: false,
|
||||
fftSize: 8192,
|
||||
fillAlpha: 1,
|
||||
frequencyScale: 'log',
|
||||
gradient: 'prism',
|
||||
gravity: 3.8,
|
||||
ledBars: true,
|
||||
linearAmplitude: true,
|
||||
linearBoost: 4,
|
||||
lineWidth: 0,
|
||||
loRes: false,
|
||||
lumiBars: false,
|
||||
maxDecibels: -25,
|
||||
maxFPS: 0,
|
||||
maxFreq: 22000,
|
||||
minDecibels: -85,
|
||||
minFreq: 20,
|
||||
mirror: 0,
|
||||
mode: 8,
|
||||
noteLabels: false,
|
||||
outlineBars: false,
|
||||
peakFadeTime: 750,
|
||||
peakHoldTime: 500,
|
||||
peakLine: false,
|
||||
presets: [],
|
||||
radial: false,
|
||||
radialInvert: false,
|
||||
radius: 0.6,
|
||||
reflexAlpha: 0.5,
|
||||
reflexBright: 1,
|
||||
reflexFit: false,
|
||||
reflexRatio: 0,
|
||||
roundBars: false,
|
||||
showFPS: false,
|
||||
showPeaks: false,
|
||||
showScaleX: false,
|
||||
showScaleY: false,
|
||||
smoothing: 0.5,
|
||||
spinSpeed: 0.5,
|
||||
splitGradient: false,
|
||||
trueLeds: false,
|
||||
volume: 1,
|
||||
weightingFilter: '',
|
||||
},
|
||||
butterchurn: {
|
||||
blendTime: 2.5,
|
||||
currentPreset: undefined,
|
||||
cyclePresets: true,
|
||||
cycleTime: 30,
|
||||
includeAllPresets: true,
|
||||
maxFPS: 0,
|
||||
randomizeNextPreset: true,
|
||||
selectedPresets: [],
|
||||
},
|
||||
type: 'audiomotionanalyzer',
|
||||
},
|
||||
window: {
|
||||
disableAutoUpdate: false,
|
||||
exitToTray: false,
|
||||
@@ -1364,6 +1543,7 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
|
||||
state.queryBuilder = resetState.queryBuilder;
|
||||
state.remote = resetState.remote;
|
||||
state.tab = resetState.tab;
|
||||
state.visualizer = resetState.visualizer;
|
||||
state.window = resetState.window;
|
||||
});
|
||||
},
|
||||
@@ -1633,3 +1813,5 @@ export const usePlayerbarSlider = () => useSettingsStore((store) => store.genera
|
||||
export const useGenreTarget = () => useSettingsStore((store) => store.general.genreTarget);
|
||||
|
||||
export const useAutoDJSettings = () => useSettingsStore((store) => store.autoDJ, shallow);
|
||||
|
||||
export const useVisualizerSettings = () => useSettingsStore((store) => store.visualizer, shallow);
|
||||
|
||||
Reference in New Issue
Block a user