mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
312 lines
13 KiB
Python
312 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Palette Generator Slaved to DSP Math
|
|
NES palette generator (proto-shader) controlled by DSP math operations on audio lines.
|
|
|
|
Architecture:
|
|
- DSP math on audio lines generates values
|
|
- Values control NES palette generator
|
|
- Palette generator affects visual output
|
|
- Feedback loop: audio → math → palette → visual
|
|
|
|
This is horrific because:
|
|
- Audio DSP math controls video palette
|
|
- Cross-domain repurposing (audio → video)
|
|
- Palette generator becomes slave to computation
|
|
|
|
This is wonderful because:
|
|
- Video synthesizer controlled by audio math
|
|
- Generative visuals from DSP operations
|
|
- Maximum retro insanity: audio math = video palette
|
|
"""
|
|
|
|
import math
|
|
from typing import List, Tuple, Dict
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# NES Palette Generator (Proto-Shader)
|
|
# Generates NES palette colors from parameters
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
@dataclass
|
|
class NESColor:
|
|
"""NES color (RGB, 2 bits per channel)"""
|
|
r: int # 0-3
|
|
g: int # 0-3
|
|
b: int # 0-3
|
|
|
|
def to_index(self) -> int:
|
|
"""Convert to NES palette index (0-63)"""
|
|
return (self.r << 4) | (self.g << 2) | self.b
|
|
|
|
@staticmethod
|
|
def from_index(index: int) -> 'NESColor':
|
|
"""Convert from NES palette index"""
|
|
return NESColor(
|
|
r=(index >> 4) & 0x03,
|
|
g=(index >> 2) & 0x03,
|
|
b=index & 0x03
|
|
)
|
|
|
|
def to_rgb888(self) -> Tuple[int, int, int]:
|
|
"""Convert to 8-bit RGB"""
|
|
return (self.r * 85, self.g * 85, self.b * 85)
|
|
|
|
class PaletteGenerator:
|
|
"""NES palette generator acting as proto-shader"""
|
|
|
|
@staticmethod
|
|
def generate_color(params: Tuple[float, float, float]) -> NESColor:
|
|
"""
|
|
Generate NES color from parameters (x, y, t).
|
|
|
|
This acts as a fragment shader - takes parameters and outputs color.
|
|
"""
|
|
x, y, t = params
|
|
|
|
# Map parameters to color channels
|
|
# x → red, y → green, t → blue
|
|
r = int((math.sin(x + t) + 1) * 1.5) % 4
|
|
g = int((math.cos(y + t) + 1) * 1.5) % 4
|
|
b = int((math.sin(x + y + t) + 1) * 1.5) % 4
|
|
|
|
return NESColor(r, g, b)
|
|
|
|
@staticmethod
|
|
def generate_palette(num_colors: int, params: List[Tuple[float, float, float]]) -> List[NESColor]:
|
|
"""Generate a palette of colors"""
|
|
palette = []
|
|
for i in range(num_colors):
|
|
if i < len(params):
|
|
color = PaletteGenerator.generate_color(params[i])
|
|
else:
|
|
# Default color if no params
|
|
color = NESColor(0, 0, 0)
|
|
palette.append(color)
|
|
return palette
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# DSP Math to Palette Control
|
|
# Map DSP math operations to palette generator parameters
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
@dataclass
|
|
class AudioSignal:
|
|
"""Audio signal from DSP math"""
|
|
frequency: float
|
|
amplitude: float
|
|
duty_cycle: float
|
|
|
|
class DSPPaletteSlave:
|
|
"""DSP math slaved to control palette generator"""
|
|
|
|
def __init__(self):
|
|
self.palette_generator = PaletteGenerator()
|
|
self.audio_history: List[AudioSignal] = []
|
|
self.palette_history: List[List[NESColor]] = []
|
|
|
|
def audio_to_palette_params(self, signal: AudioSignal) -> Tuple[float, float, float]:
|
|
"""
|
|
Convert audio signal to palette generator parameters.
|
|
|
|
Maps: frequency → x, amplitude → y, duty_cycle → t
|
|
"""
|
|
# Normalize parameters to 0-2π range
|
|
x = signal.frequency / 1000.0 * math.pi # Normalize freq
|
|
y = signal.amplitude * 2 * math.pi # Normalize amplitude
|
|
t = signal.duty_cycle * 2 * math.pi # Normalize duty
|
|
|
|
return (x, y, t)
|
|
|
|
def dsp_to_palette(self, signals: List[AudioSignal]) -> List[NESColor]:
|
|
"""
|
|
Convert DSP audio signals to palette.
|
|
|
|
Each audio signal controls one palette entry.
|
|
"""
|
|
params_list = [self.audio_to_palette_params(s) for s in signals]
|
|
palette = self.palette_generator.generate_palette(len(signals), params_list)
|
|
|
|
self.audio_history = signals
|
|
self.palette_history.append(palette)
|
|
|
|
return palette
|
|
|
|
def dsp_operation_to_palette(self, operation: str, signals: List[AudioSignal]) -> List[NESColor]:
|
|
"""
|
|
Perform DSP operation and convert result to palette.
|
|
|
|
Operations: add, multiply, integrate, differentiate
|
|
"""
|
|
if operation == "add":
|
|
# Add signals: combine amplitudes
|
|
combined_amp = sum(s.amplitude for s in signals)
|
|
combined_freq = sum(s.frequency for s in signals) / len(signals)
|
|
combined_duty = sum(s.duty_cycle for s in signals) / len(signals)
|
|
combined = AudioSignal(combined_freq, combined_amp, combined_duty)
|
|
return self.dsp_to_palette([combined])
|
|
|
|
elif operation == "multiply":
|
|
# Multiply signals: AM modulation
|
|
if len(signals) >= 2:
|
|
mul_amp = signals[0].amplitude * signals[1].amplitude
|
|
mul_freq = math.sqrt(signals[0].frequency * signals[1].frequency)
|
|
mul_duty = signals[0].duty_cycle if signals[0].duty_cycle >= 0.5 else 1.0 - signals[0].duty_cycle
|
|
mul_duty *= signals[1].duty_cycle if signals[1].duty_cycle >= 0.5 else 1.0 - signals[1].duty_cycle
|
|
combined = AudioSignal(mul_freq, mul_amp, mul_duty)
|
|
return self.dsp_to_palette([combined])
|
|
|
|
elif operation == "integrate":
|
|
# Integrate: accumulate over history
|
|
if self.audio_history:
|
|
integrated = AudioSignal(
|
|
frequency=sum(s.frequency for s in self.audio_history + signals),
|
|
amplitude=sum(s.amplitude for s in self.audio_history + signals),
|
|
duty_cycle=sum(s.duty_cycle for s in self.audio_history + signals) / (len(self.audio_history) + len(signals))
|
|
)
|
|
return self.dsp_to_palette([integrated])
|
|
|
|
elif operation == "differentiate":
|
|
# Differentiate: rate of change
|
|
if len(signals) >= 2:
|
|
diff = AudioSignal(
|
|
frequency=signals[-1].frequency - signals[-2].frequency,
|
|
amplitude=signals[-1].amplitude - signals[-2].amplitude,
|
|
duty_cycle=signals[-1].duty_cycle - signals[-2].duty_cycle
|
|
)
|
|
return self.dsp_to_palette([diff])
|
|
|
|
# Default: direct conversion
|
|
return self.dsp_to_palette(signals)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# Video Synthesizer
|
|
# Audio DSP math controls video palette generation
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
class VideoSynthesizer:
|
|
"""Video synthesizer controlled by audio DSP math"""
|
|
|
|
def __init__(self):
|
|
self.dsp_slave = DSPPaletteSlave()
|
|
self.frame_count = 0
|
|
|
|
def generate_frame(self, audio_signals: List[AudioSignal],
|
|
dsp_operation: str = "none") -> List[NESColor]:
|
|
"""
|
|
Generate video frame from audio DSP math.
|
|
|
|
Audio signals → DSP operation → Palette → Video frame
|
|
"""
|
|
self.frame_count += 1
|
|
|
|
if dsp_operation == "none":
|
|
palette = self.dsp_slave.dsp_to_palette(audio_signals)
|
|
else:
|
|
palette = self.dsp_slave.dsp_operation_to_palette(dsp_operation, audio_signals)
|
|
|
|
return palette
|
|
|
|
def animate(self, base_signals: List[AudioSignal],
|
|
frames: int, dsp_operation: str = "none") -> List[List[NESColor]]:
|
|
"""
|
|
Generate animation frames.
|
|
|
|
Modulates audio signals over time to create animated palette.
|
|
"""
|
|
animation = []
|
|
|
|
for frame in range(frames):
|
|
# Modulate signals over time
|
|
modulated = []
|
|
for i, signal in enumerate(base_signals):
|
|
modulated_signal = AudioSignal(
|
|
frequency=signal.frequency * (1 + 0.1 * math.sin(frame * 0.1 + i)),
|
|
amplitude=signal.amplitude * (1 + 0.1 * math.cos(frame * 0.1 + i)),
|
|
duty_cycle=(signal.duty_cycle + 0.01 * math.sin(frame * 0.05 + i)) % 1.0
|
|
)
|
|
modulated.append(modulated_signal)
|
|
|
|
frame_palette = self.generate_frame(modulated, dsp_operation)
|
|
animation.append(frame_palette)
|
|
|
|
return animation
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
# Test / Demo
|
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
def run_test():
|
|
"""Run palette generator slave to DSP math test"""
|
|
print("=" * 70)
|
|
print("PALETTE GENERATOR SLAVED TO DSP MATH")
|
|
print("=" * 70)
|
|
|
|
print("\n[*] Architecture:")
|
|
print(" Audio DSP math → Palette generator parameters → Video palette")
|
|
print(" Frequency → x (red channel)")
|
|
print(" Amplitude → y (green channel)")
|
|
print(" Duty cycle → t (blue channel)")
|
|
print(" DSP operations (add, multiply, integrate) modulate palette")
|
|
|
|
synthesizer = VideoSynthesizer()
|
|
|
|
# Create base audio signals
|
|
print("\n[*] Creating base audio signals...")
|
|
base_signals = [
|
|
AudioSignal(frequency=440.0, amplitude=0.5, duty_cycle=0.5),
|
|
AudioSignal(frequency=880.0, amplitude=0.7, duty_cycle=0.25),
|
|
AudioSignal(frequency=220.0, amplitude=0.3, duty_cycle=0.75),
|
|
]
|
|
print(f" Signal 1: {base_signals[0].frequency}Hz, amp={base_signals[0].amplitude}, duty={base_signals[0].duty_cycle}")
|
|
print(f" Signal 2: {base_signals[1].frequency}Hz, amp={base_signals[1].amplitude}, duty={base_signals[1].duty_cycle}")
|
|
print(f" Signal 3: {base_signals[2].frequency}Hz, amp={base_signals[2].amplitude}, duty={base_signals[2].duty_cycle}")
|
|
|
|
# Direct conversion
|
|
print("\n[*] Direct audio-to-palette conversion...")
|
|
palette = synthesizer.generate_frame(base_signals, "none")
|
|
print(" Palette generated:")
|
|
for i, color in enumerate(palette):
|
|
rgb = color.to_rgb888()
|
|
print(f" Entry {i}: R={rgb[0]}, G={rgb[1]}, B={rgb[2]} (index={color.to_index()})")
|
|
|
|
# DSP addition
|
|
print("\n[*] DSP addition (add signals)...")
|
|
palette_add = synthesizer.generate_frame(base_signals, "add")
|
|
print(" Palette after addition:")
|
|
for i, color in enumerate(palette_add):
|
|
rgb = color.to_rgb888()
|
|
print(f" Entry {i}: R={rgb[0]}, G={rgb[1]}, B={rgb[2]} (index={color.to_index()})")
|
|
|
|
# DSP multiplication
|
|
print("\n[*] DSP multiplication (AM modulation)...")
|
|
palette_mul = synthesizer.generate_frame(base_signals, "multiply")
|
|
print(" Palette after multiplication:")
|
|
for i, color in enumerate(palette_mul):
|
|
rgb = color.to_rgb888()
|
|
print(f" Entry {i}: R={rgb[0]}, G={rgb[1]}, B={rgb[2]} (index={color.to_index()})")
|
|
|
|
# Animation
|
|
print("\n[*] Generating 10-frame animation...")
|
|
animation = synthesizer.animate(base_signals, frames=10, dsp_operation="multiply")
|
|
print(f" Animation generated: {len(animation)} frames")
|
|
print(" Frame 0 palette:")
|
|
for i, color in enumerate(animation[0]):
|
|
rgb = color.to_rgb888()
|
|
print(f" Entry {i}: R={rgb[0]}, G={rgb[1]}, B={rgb[2]}")
|
|
print(" Frame 5 palette:")
|
|
for i, color in enumerate(animation[5]):
|
|
rgb = color.to_rgb888()
|
|
print(f" Entry {i}: R={rgb[0]}, G={rgb[1]}, B={rgb[2]}")
|
|
|
|
print("\n" + "=" * 70)
|
|
print("PALETTE-DSP SLAVE SYSTEM COMPLETE")
|
|
print("=" * 70)
|
|
print("\n[*] Horrific: Audio DSP math controls video palette")
|
|
print("[*] Wonderful: Video synthesizer from audio computation")
|
|
print("[*] Maximum retro insanity: audio math = video palette")
|
|
|
|
if __name__ == "__main__":
|
|
run_test()
|