slightly better
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user