Upload truthlightecho.py with huggingface_hub
Browse files- truthlightecho.py +302 -0
truthlightecho.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
LYGO TruthLightEcho v0.1
|
| 4 |
+
Generates harmonic echo sequences from ∫Truth×Light metrics.
|
| 5 |
+
|
| 6 |
+
Integrates with LYGO RESONANCE engine (falls back to built-in harmonic echo synth).
|
| 7 |
+
Ingests or computes Truth × Light (from images via contrast/brightness, or JSON profiles from Glyph2Resonance/FractalWeaver using their truth_light/phi/recursive_harmony fields).
|
| 8 |
+
Maps the integral to:
|
| 9 |
+
- Harmonic series and intervals (purer with higher truth/light).
|
| 10 |
+
- Echo count, recursive delays, and self-similar spacing (echoing the fractal/glyph recursion).
|
| 11 |
+
- Decay envelopes and modulations (light quality for brightness/sweep, truth for harmonic stability/richness).
|
| 12 |
+
- Evolution: Echoes that build, ring, and recur with increasing/decreasing complexity based on the score.
|
| 13 |
+
|
| 14 |
+
Outputs:
|
| 15 |
+
- Stereo WAV of the harmonic echo sequence (default 30-90s).
|
| 16 |
+
- .truthlight.echo.json profile with the integral, echo structure, and LYGO mappings.
|
| 17 |
+
- Optional stems (dry + echo layers), MIDI with harmonic echo notes.
|
| 18 |
+
|
| 19 |
+
Usage examples:
|
| 20 |
+
python truthlightecho.py my_glyph_profile.json --preset pure-light --seed 963 --duration 60
|
| 21 |
+
python truthlightecho.py my_fractal.png --preset truth-echo
|
| 22 |
+
python truthlightecho.py --batch ./profiles/ --preset light-unfold
|
| 23 |
+
|
| 24 |
+
Ties to LYGO ecosystem:
|
| 25 |
+
- Companion to lygo-resonance, lygo-glyph2resonance (#1), lygo-fractalweaver (#2).
|
| 26 |
+
- Army-ready (truthlight-echo roles + champions like LYRA, SEPHRAEL, ARKOS).
|
| 27 |
+
- Grows to 3-Brain as harmonic truth/light nodes.
|
| 28 |
+
- P0/Oath/Guardian aware: local-first, seed-locked, review before external.
|
| 29 |
+
|
| 30 |
+
Full instructions in SKILL.md. Links to Resonance site and donation included.
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
import cv2
|
| 34 |
+
import numpy as np
|
| 35 |
+
import soundfile as sf
|
| 36 |
+
import math
|
| 37 |
+
import argparse
|
| 38 |
+
import json
|
| 39 |
+
from pathlib import Path
|
| 40 |
+
from datetime import datetime
|
| 41 |
+
from typing import Dict, Any, Optional
|
| 42 |
+
import sys
|
| 43 |
+
|
| 44 |
+
try:
|
| 45 |
+
from resonance_engine import ResonanceEngine, PRESETS
|
| 46 |
+
HAS_FULL_ENGINE = True
|
| 47 |
+
except ImportError:
|
| 48 |
+
HAS_FULL_ENGINE = False
|
| 49 |
+
PRESETS = {
|
| 50 |
+
"pure-light": {"noise_vol": 0.03, "drone_vol": 0.10, "note_vol": 0.14, "glitch_vol": 0.01},
|
| 51 |
+
"truth-echo": {"noise_vol": 0.04, "drone_vol": 0.09, "note_vol": 0.12, "glitch_vol": 0.02},
|
| 52 |
+
"light-unfold": {"noise_vol": 0.05, "drone_vol": 0.08, "note_vol": 0.13, "glitch_vol": 0.015},
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
__version__ = "0.1.0"
|
| 56 |
+
|
| 57 |
+
def compute_truth_light_from_image(image_path: str) -> Dict[str, Any]:
|
| 58 |
+
"""Compute Truth × Light proxy from image (contrast × brightness, plus harmony cues)."""
|
| 59 |
+
img = cv2.imread(str(image_path))
|
| 60 |
+
if img is None:
|
| 61 |
+
raise FileNotFoundError(f"Could not load image: {image_path}")
|
| 62 |
+
|
| 63 |
+
if len(img.shape) == 2:
|
| 64 |
+
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
|
| 65 |
+
|
| 66 |
+
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
| 67 |
+
contrast = float(np.std(gray) / 255.0)
|
| 68 |
+
brightness = float(np.mean(gray) / 255.0)
|
| 69 |
+
truth_light = round(contrast * brightness, 4)
|
| 70 |
+
|
| 71 |
+
# Additional cues from prior tools' style (phi-like, symmetry via edges)
|
| 72 |
+
edges = cv2.Canny(gray, 50, 150)
|
| 73 |
+
edge_density = np.sum(edges > 0) / (gray.shape[0] * gray.shape[1])
|
| 74 |
+
harmony_cue = round(min(edge_density * 5, 1.0), 4) # rough stand-in
|
| 75 |
+
|
| 76 |
+
return {
|
| 77 |
+
"source": Path(image_path).name,
|
| 78 |
+
"truth_light": truth_light,
|
| 79 |
+
"contrast": round(contrast, 4),
|
| 80 |
+
"brightness": round(brightness, 4),
|
| 81 |
+
"harmony_cue": harmony_cue,
|
| 82 |
+
"edge_density": round(edge_density, 5),
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
def ingest_truth_light_from_profile(profile_path: str) -> Dict[str, Any]:
|
| 86 |
+
"""Ingest from JSON profile (Glyph2Resonance, FractalWeaver, or similar)."""
|
| 87 |
+
with open(profile_path, "r", encoding="utf-8") as f:
|
| 88 |
+
data = json.load(f)
|
| 89 |
+
|
| 90 |
+
# Look for fields from prior tools
|
| 91 |
+
tl = 0.5
|
| 92 |
+
if "LYGO_GLYPH2RESONANCE" in data:
|
| 93 |
+
lygo = data["LYGO_GLYPH2RESONANCE"]["lygo_mappings"]
|
| 94 |
+
tl = lygo.get("truth_light", 0.5)
|
| 95 |
+
phi = lygo.get("phi_resonance", 0.5)
|
| 96 |
+
harmony = lygo.get("seal_symmetry", 0.5)
|
| 97 |
+
elif "LYGO_FRACTALWEAVER" in data:
|
| 98 |
+
lygo = data["LYGO_FRACTALWEAVER"]["lygo_mappings"]
|
| 99 |
+
tl = lygo.get("truth_light", 0.5) if "truth_light" in lygo else 0.5
|
| 100 |
+
phi = lygo.get("self_similarity", 0.5)
|
| 101 |
+
harmony = lygo.get("recursive_harmony", 0.5)
|
| 102 |
+
else:
|
| 103 |
+
# Generic fallback
|
| 104 |
+
tl = data.get("truth_light", data.get("LYGO_PROFILE", {}).get("truth_light", 0.5))
|
| 105 |
+
phi = data.get("phi_resonance", 0.5)
|
| 106 |
+
harmony = data.get("harmony", 0.5)
|
| 107 |
+
|
| 108 |
+
composite = round((tl + phi + harmony) / 3.0, 4)
|
| 109 |
+
return {
|
| 110 |
+
"source": Path(profile_path).name,
|
| 111 |
+
"truth_light": round(tl, 4),
|
| 112 |
+
"composite_truth_light": composite,
|
| 113 |
+
"phi_resonance": round(phi, 4),
|
| 114 |
+
"harmony": round(harmony, 4),
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
def map_to_harmonic_echo_params(truth_light_data: Dict[str, Any], preset: str = "pure-light") -> Dict[str, Any]:
|
| 118 |
+
"""Map Truth × Light to base harmonic echo params + evolution."""
|
| 119 |
+
tl = truth_light_data.get("composite_truth_light", truth_light_data.get("truth_light", 0.5))
|
| 120 |
+
phi = truth_light_data.get("phi_resonance", 0.5)
|
| 121 |
+
harm = truth_light_data.get("harmony", 0.5)
|
| 122 |
+
|
| 123 |
+
base_root = 220 + (tl * 200) # higher truth/light = higher, brighter root
|
| 124 |
+
base_theta = 4.0 + (phi * 4) # phi for interval richness
|
| 125 |
+
|
| 126 |
+
if preset == "pure-light":
|
| 127 |
+
echo_count = int(4 + tl * 8)
|
| 128 |
+
delay_base = 0.15 + (1 - tl) * 0.2 # shorter delays for higher light
|
| 129 |
+
decay = 0.85 + tl * 0.1
|
| 130 |
+
harmonic_richness = 0.7 + phi * 0.5
|
| 131 |
+
evolution_rate = 0.4 + harm * 0.6
|
| 132 |
+
elif preset == "truth-echo":
|
| 133 |
+
echo_count = int(5 + harm * 6)
|
| 134 |
+
delay_base = 0.2 + (1 - harm) * 0.15
|
| 135 |
+
decay = 0.8 + harm * 0.12
|
| 136 |
+
harmonic_richness = 0.6 + tl * 0.4
|
| 137 |
+
evolution_rate = 0.5 + tl * 0.5
|
| 138 |
+
else: # light-unfold
|
| 139 |
+
echo_count = int(3 + tl * 10)
|
| 140 |
+
delay_base = 0.1 + (1 - tl) * 0.25
|
| 141 |
+
decay = 0.9 + tl * 0.08
|
| 142 |
+
harmonic_richness = 0.8 + harm * 0.4
|
| 143 |
+
evolution_rate = 0.3 + harm * 0.7
|
| 144 |
+
|
| 145 |
+
base_config = {
|
| 146 |
+
"sr": 44100,
|
| 147 |
+
"duration": 60.0,
|
| 148 |
+
"root_freq": base_root,
|
| 149 |
+
"theta": base_theta,
|
| 150 |
+
"echo_count": max(3, min(echo_count, 12)),
|
| 151 |
+
"delay_base": delay_base,
|
| 152 |
+
"decay": min(decay, 0.98),
|
| 153 |
+
"harmonic_richness": min(harmonic_richness, 1.0),
|
| 154 |
+
"evolution_rate": evolution_rate,
|
| 155 |
+
"noise_vol": 0.02,
|
| 156 |
+
"drone_vol": 0.06 + tl * 0.04,
|
| 157 |
+
"note_vol": 0.08 + phi * 0.05,
|
| 158 |
+
"glitch_vol": 0.01 + (1 - harm) * 0.02,
|
| 159 |
+
"random_seed": None,
|
| 160 |
+
"verbose": True,
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
lygo_meta = {
|
| 164 |
+
"integral_truth_light": round(tl, 4),
|
| 165 |
+
"echo_count": base_config["echo_count"],
|
| 166 |
+
"harmonic_intervals": [1.0, 1.5, 2.0, 2.5, 3.0][:base_config["echo_count"]-1], # simplified from phi/harmony
|
| 167 |
+
"recursive_decay": round(base_config["decay"], 3),
|
| 168 |
+
"evolution_rate": round(evolution_rate, 2),
|
| 169 |
+
"suggested_duration": base_config["duration"],
|
| 170 |
+
"preset_used": preset,
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
return base_config, lygo_meta
|
| 174 |
+
|
| 175 |
+
def synthesize_harmonic_echoes(truth_light_data: Dict, config: Dict, output_wav: str):
|
| 176 |
+
"""Built-in harmonic echo synth with recursive self-similar structure (fallback)."""
|
| 177 |
+
sr = config["sr"]
|
| 178 |
+
dur = config.get("duration", 60.0)
|
| 179 |
+
n = int(sr * dur)
|
| 180 |
+
audio = np.zeros(n, dtype=np.float32)
|
| 181 |
+
|
| 182 |
+
root = config["root_freq"]
|
| 183 |
+
theta = config["theta"]
|
| 184 |
+
echo_count = config["echo_count"]
|
| 185 |
+
delay_base = config["delay_base"]
|
| 186 |
+
decay = config["decay"]
|
| 187 |
+
rich = config["harmonic_richness"]
|
| 188 |
+
evo = config["evolution_rate"]
|
| 189 |
+
|
| 190 |
+
# Base drone layer
|
| 191 |
+
t = np.linspace(0, dur, n, False, dtype=np.float32)
|
| 192 |
+
drone = np.sin(2 * np.pi * root * t).astype(np.float32)
|
| 193 |
+
audio += drone * config["drone_vol"]
|
| 194 |
+
|
| 195 |
+
# Harmonic echo layers (recursive delays and intervals)
|
| 196 |
+
for e in range(echo_count):
|
| 197 |
+
interval = 1.0 + (e * (0.5 + rich * 0.3)) # harmonic-ish
|
| 198 |
+
delay = delay_base * (1 + e * (0.3 + evo * 0.2)) # self-similar spacing
|
| 199 |
+
echo_start = int(delay * sr)
|
| 200 |
+
if echo_start >= n:
|
| 201 |
+
break
|
| 202 |
+
|
| 203 |
+
echo_len = n - echo_start
|
| 204 |
+
t_echo = np.linspace(0, echo_len / sr, echo_len, False, dtype=np.float32)
|
| 205 |
+
harm_freq = root * interval
|
| 206 |
+
echo = np.sin(2 * np.pi * harm_freq * t_echo).astype(np.float32)
|
| 207 |
+
|
| 208 |
+
# Evolving amplitude (build then decay, modulated by truth/light)
|
| 209 |
+
amp = (config["note_vol"] * (rich ** e)) * (decay ** (e * 2))
|
| 210 |
+
# Add evolution: some echoes "unfold" or "fade" over time
|
| 211 |
+
env = np.linspace(0.6, 1.0, echo_len) * np.linspace(1.0, 0.3, echo_len)
|
| 212 |
+
echo *= amp * env
|
| 213 |
+
|
| 214 |
+
audio[echo_start:echo_start + echo_len] += echo[:echo_len]
|
| 215 |
+
|
| 216 |
+
# Subtle noise/glitch for "light" texture (controlled by score)
|
| 217 |
+
if config["glitch_vol"] > 0:
|
| 218 |
+
noise = np.random.uniform(-0.5, 0.5, n).astype(np.float32)
|
| 219 |
+
audio += noise * config["glitch_vol"] * (0.5 + evo * 0.5)
|
| 220 |
+
|
| 221 |
+
# Polish
|
| 222 |
+
audio = np.tanh(audio * 1.4) / 1.4
|
| 223 |
+
peak = np.max(np.abs(audio))
|
| 224 |
+
if peak > 0:
|
| 225 |
+
audio = (audio / peak * 0.96).astype(np.float32)
|
| 226 |
+
|
| 227 |
+
sf.write(output_wav, audio, sr)
|
| 228 |
+
return output_wav
|
| 229 |
+
|
| 230 |
+
def main():
|
| 231 |
+
parser = argparse.ArgumentParser(
|
| 232 |
+
description="LYGO TruthLightEcho — Generate harmonic echo sequences from ∫Truth×Light"
|
| 233 |
+
)
|
| 234 |
+
parser.add_argument("input", nargs="?", help="Image or .json profile from prior tools (Glyph2Resonance, FractalWeaver, etc.)")
|
| 235 |
+
parser.add_argument("--preset", choices=["pure-light", "truth-echo", "light-unfold"], default="pure-light")
|
| 236 |
+
parser.add_argument("--seed", type=int, default=None)
|
| 237 |
+
parser.add_argument("--duration", type=float, default=60.0)
|
| 238 |
+
parser.add_argument("-o", "--output", default=None)
|
| 239 |
+
parser.add_argument("--profile", default=None)
|
| 240 |
+
parser.add_argument("--batch", action="store_true")
|
| 241 |
+
parser.add_argument("--truth-light", type=float, default=None, help="Manual Truth × Light score (0-1)")
|
| 242 |
+
args = parser.parse_args()
|
| 243 |
+
|
| 244 |
+
if not args.input and args.truth_light is None:
|
| 245 |
+
parser.print_help()
|
| 246 |
+
return
|
| 247 |
+
|
| 248 |
+
if args.input:
|
| 249 |
+
inp = Path(args.input)
|
| 250 |
+
if inp.suffix.lower() in [".json"]:
|
| 251 |
+
tl_data = ingest_truth_light_from_profile(str(inp))
|
| 252 |
+
else:
|
| 253 |
+
tl_data = compute_truth_light_from_image(str(inp))
|
| 254 |
+
else:
|
| 255 |
+
tl_data = {"truth_light": args.truth_light, "composite_truth_light": args.truth_light, "source": "manual"}
|
| 256 |
+
|
| 257 |
+
config, lygo_meta = map_to_harmonic_echo_params(tl_data, args.preset)
|
| 258 |
+
|
| 259 |
+
if args.seed is not None:
|
| 260 |
+
config["random_seed"] = args.seed
|
| 261 |
+
config["duration"] = args.duration
|
| 262 |
+
|
| 263 |
+
wav_path = args.output or f"truthlightecho_{Path(args.input).stem if args.input else 'manual'}.wav"
|
| 264 |
+
json_path = args.profile or f"truthlightecho_{Path(args.input).stem if args.input else 'manual'}.truthlight.echo.json"
|
| 265 |
+
|
| 266 |
+
if HAS_FULL_ENGINE:
|
| 267 |
+
# Could extend engine for echoes; using built-in for now with full control
|
| 268 |
+
pass
|
| 269 |
+
synthesize_harmonic_echoes(tl_data, config, wav_path)
|
| 270 |
+
|
| 271 |
+
full_profile = {
|
| 272 |
+
"LYGO_TRUTHLIGHTECHO": {
|
| 273 |
+
"version": __version__,
|
| 274 |
+
"source_input": str(args.input) if args.input else "manual",
|
| 275 |
+
"preset": args.preset,
|
| 276 |
+
"truth_light_data": tl_data,
|
| 277 |
+
"audio_config": config,
|
| 278 |
+
"lygo_mappings": lygo_meta,
|
| 279 |
+
"generated_at": datetime.now().isoformat(),
|
| 280 |
+
"reproducible_with_seed": args.seed,
|
| 281 |
+
}
|
| 282 |
+
}
|
| 283 |
+
with open(json_path, "w", encoding="utf-8") as f:
|
| 284 |
+
json.dump(full_profile, f, indent=2)
|
| 285 |
+
|
| 286 |
+
print(f"✓ Harmonic echo sequence: {wav_path}")
|
| 287 |
+
print(f"✓ Profile: {json_path}")
|
| 288 |
+
print(f" integral={lygo_meta['integral_truth_light']}, echoes={lygo_meta['echo_count']}, recursive_decay={lygo_meta['recursive_decay']}")
|
| 289 |
+
|
| 290 |
+
# Grow to 3-Brain
|
| 291 |
+
try:
|
| 292 |
+
sys.path.insert(0, str(Path.cwd()))
|
| 293 |
+
from lyra_brain import LyraThreeBrainMemory
|
| 294 |
+
brain = LyraThreeBrainMemory(base_dir=Path.cwd(), use_advanced=True)
|
| 295 |
+
summary = f"TruthLightEcho: {Path(args.input).name if args.input else 'manual'} → {args.preset} harmonic echoes | integral={lygo_meta['integral_truth_light']} decay={lygo_meta['recursive_decay']}"
|
| 296 |
+
nid = brain.grow(summary, source="truthlightecho")
|
| 297 |
+
print(f" Grown to 3-Brain node: {nid}")
|
| 298 |
+
except Exception:
|
| 299 |
+
pass
|
| 300 |
+
|
| 301 |
+
if __name__ == "__main__":
|
| 302 |
+
main()
|