slightly better

This commit is contained in:
2025-10-06 02:16:23 +02:00
parent ba37b94908
commit ac772054c9
35 changed files with 1874 additions and 390 deletions

View File

@@ -291,7 +291,13 @@ export function generateTileGrid(rows: number, cols: number, complexity: number
const row: TileState[] = []
for (let j = 0; j < cols; j++) {
const formula = generateRandomFormula(complexity)
row.push(createTileState(formula))
const tile = createTileState(formula)
tile.engineParams.a = Math.floor(Math.random() * 256)
tile.engineParams.b = Math.floor(Math.random() * 256)
tile.engineParams.c = Math.floor(Math.random() * 256)
tile.engineParams.d = Math.floor(Math.random() * 256)
tile.engineParams.pitch = 0.1 + Math.random() * 0.9
row.push(tile)
}
grid.push(row)
}

View File

@@ -1,16 +1,19 @@
import type { TileState } from '../types/tiles'
import { engineSettings, effectSettings } from '../stores/settings'
import { getDefaultEngineValues, getDefaultEffectValues } from '../config/effects'
import { engineSettings, effectSettings, lfoSettings, getDefaultLFOValues } from '../stores/settings'
import { getDefaultEngineValues, getDefaultEffectValues, ENGINE_CONTROLS, EFFECTS } from '../config/effects'
import type { LFOSettings } from '../stores/settings'
export function createTileState(
formula: string,
engineParams?: Record<string, number>,
effectParams?: Record<string, number | boolean | string>
effectParams?: Record<string, number | boolean | string>,
lfoConfigs?: LFOSettings
): TileState {
return {
formula,
engineParams: engineParams ?? { ...getDefaultEngineValues() },
effectParams: effectParams ?? { ...getDefaultEffectValues(), masterVolume: 75 }
effectParams: effectParams ?? { ...getDefaultEffectValues(), masterVolume: 75 },
lfoConfigs: lfoConfigs ?? getDefaultLFOValues()
}
}
@@ -18,7 +21,8 @@ export function createTileStateFromCurrent(formula: string): TileState {
return {
formula,
engineParams: { ...engineSettings.get() },
effectParams: { ...effectSettings.get() }
effectParams: { ...effectSettings.get() },
lfoConfigs: JSON.parse(JSON.stringify(lfoSettings.get()))
}
}
@@ -30,13 +34,20 @@ export function loadTileParams(tile: TileState): void {
Object.entries(tile.effectParams).forEach(([key, value]) => {
effectSettings.setKey(key as any, value as any)
})
if (tile.lfoConfigs) {
Object.entries(tile.lfoConfigs).forEach(([key, value]) => {
lfoSettings.setKey(key as any, value)
})
}
}
export function saveTileParams(tile: TileState): TileState {
return {
...tile,
engineParams: { ...engineSettings.get() },
effectParams: { ...effectSettings.get() }
effectParams: { ...effectSettings.get() },
lfoConfigs: JSON.parse(JSON.stringify(lfoSettings.get()))
}
}
@@ -44,6 +55,126 @@ export function cloneTileState(tile: TileState): TileState {
return {
formula: tile.formula,
engineParams: { ...tile.engineParams },
effectParams: { ...tile.effectParams }
effectParams: { ...tile.effectParams },
lfoConfigs: JSON.parse(JSON.stringify(tile.lfoConfigs))
}
}
function randomInRange(min: number, max: number, step: number): number {
const steps = Math.floor((max - min) / step)
const randomStep = Math.floor(Math.random() * (steps + 1))
return min + randomStep * step
}
export function randomizeTileParams(tile: TileState): TileState {
const randomEngineParams: Record<string, number> = {}
const randomEffectParams: Record<string, number | boolean | string> = {}
ENGINE_CONTROLS.forEach(control => {
control.parameters.forEach(param => {
if (param.id === 'sampleRate') {
randomEngineParams[param.id] = param.max as number
} else if (param.id === 'bitDepth') {
randomEngineParams[param.id] = param.max as number
} else if (param.id === 'pitch') {
randomEngineParams[param.id] = 0.1 + Math.random() * 1.4
} else if (param.id === 'a' || param.id === 'b' || param.id === 'c' || param.id === 'd') {
randomEngineParams[param.id] = Math.floor(Math.random() * 256)
} else {
randomEngineParams[param.id] = randomInRange(
param.min as number,
param.max as number,
param.step as number
)
}
})
})
const filterModes = ['lowpass', 'highpass']
const selectedFilterMode = filterModes[Math.floor(Math.random() * filterModes.length)]
const filterFreq = selectedFilterMode === 'lowpass'
? 800 + Math.random() * 4200
: 100 + Math.random() * 700
randomEffectParams['filterMode'] = selectedFilterMode
randomEffectParams['filterFreq'] = filterFreq
EFFECTS.forEach(effect => {
effect.parameters.forEach(param => {
if (param.id === 'filterMode' || param.id === 'filterFreq') {
return
}
if (param.id === 'delayWetDry') {
randomEffectParams[param.id] = Math.random() * 50
} else if (param.id === 'delayFeedback') {
randomEffectParams[param.id] = Math.random() * 90
} else if (param.id === 'bitcrushDepth') {
randomEffectParams[param.id] = 12 + Math.floor(Math.random() * 5)
} else if (param.id === 'bitcrushRate') {
randomEffectParams[param.id] = Math.random() * 30
} else if (param.options) {
const options = param.options
const randomOption = options[Math.floor(Math.random() * options.length)]
randomEffectParams[param.id] = randomOption.value
} else if (typeof param.default === 'boolean') {
randomEffectParams[param.id] = Math.random() > 0.5
} else {
randomEffectParams[param.id] = randomInRange(
param.min as number,
param.max as number,
param.step as number
)
}
})
if (effect.bypassable) {
randomEffectParams[`${effect.id}Bypass`] = Math.random() > 0.5
}
})
const modulatableParams = [
'filterFreq', 'filterRes',
'wavefolderDrive', 'bitcrushDepth', 'bitcrushRate',
'delayTime', 'delayFeedback', 'delayWetDry',
'reverbWetDry', 'reverbDecay', 'reverbDamping'
]
const randomLFOConfigs = getDefaultLFOValues()
const waveforms: Array<'sine' | 'triangle' | 'square' | 'sawtooth'> = ['sine', 'triangle', 'square', 'sawtooth']
randomLFOConfigs.lfo1 = {
waveform: waveforms[Math.floor(Math.random() * waveforms.length)],
frequency: Math.random() * 10,
phase: Math.random() * 360,
mappings: [{
targetParam: 'filterFreq',
depth: 20 + Math.random() * 60
}]
}
const availableParams = modulatableParams.filter(p => p !== 'filterFreq')
const lfoConfigs = [randomLFOConfigs.lfo2, randomLFOConfigs.lfo3, randomLFOConfigs.lfo4]
lfoConfigs.forEach(config => {
if (availableParams.length > 0 && Math.random() > 0.3) {
const paramIndex = Math.floor(Math.random() * availableParams.length)
const targetParam = availableParams.splice(paramIndex, 1)[0]
config.waveform = waveforms[Math.floor(Math.random() * waveforms.length)]
config.frequency = Math.random() * 10
config.phase = Math.random() * 360
config.mappings = [{
targetParam,
depth: 20 + Math.random() * 60
}]
}
})
return {
formula: tile.formula,
engineParams: randomEngineParams,
effectParams: randomEffectParams,
lfoConfigs: randomLFOConfigs
}
}

View File

@@ -10,24 +10,19 @@ export function generateWaveformData(formula: string, width: number, sampleRate:
for (let s = 0; s < samplesPerPixel; s++) {
const t = x * samplesPerPixel + s
try {
const value = compiledFormula(t, a, b, c, d)
const byteValue = value & 0xFF
const normalized = (byteValue - 128) / 128
min = Math.min(min, normalized)
max = Math.max(max, normalized)
} catch {
min = Math.min(min, 0)
max = Math.max(max, 0)
}
const value = compiledFormula(t, a, b, c, d)
const byteValue = value & 0xFF
const normalized = (byteValue - 128) / 128
min = Math.min(min, normalized)
max = Math.max(max, normalized)
}
waveform.push(min, max)
}
return waveform
} catch {
return new Array(width * 2).fill(0)
} catch (error) {
return Array(width * 2).fill(0)
}
}