| |
| """ |
| LYGO TruthLightEcho v0.1 |
| Generates harmonic echo sequences from ∫Truth×Light metrics. |
| |
| Integrates with LYGO RESONANCE engine (falls back to built-in harmonic echo synth). |
| Ingests or computes Truth × Light (from images via contrast/brightness, or JSON profiles from Glyph2Resonance/FractalWeaver using their truth_light/phi/recursive_harmony fields). |
| Maps the integral to: |
| - Harmonic series and intervals (purer with higher truth/light). |
| - Echo count, recursive delays, and self-similar spacing (echoing the fractal/glyph recursion). |
| - Decay envelopes and modulations (light quality for brightness/sweep, truth for harmonic stability/richness). |
| - Evolution: Echoes that build, ring, and recur with increasing/decreasing complexity based on the score. |
| |
| Outputs: |
| - Stereo WAV of the harmonic echo sequence (default 30-90s). |
| - .truthlight.echo.json profile with the integral, echo structure, and LYGO mappings. |
| - Optional stems (dry + echo layers), MIDI with harmonic echo notes. |
| |
| Usage examples: |
| python truthlightecho.py my_glyph_profile.json --preset pure-light --seed 963 --duration 60 |
| python truthlightecho.py my_fractal.png --preset truth-echo |
| python truthlightecho.py --batch ./profiles/ --preset light-unfold |
| |
| Ties to LYGO ecosystem: |
| - Companion to lygo-resonance, lygo-glyph2resonance (#1), lygo-fractalweaver (#2). |
| - Army-ready (truthlight-echo roles + champions like LYRA, SEPHRAEL, ARKOS). |
| - Grows to 3-Brain as harmonic truth/light nodes. |
| - P0/Oath/Guardian aware: local-first, seed-locked, review before external. |
| |
| Full instructions in SKILL.md. Links to Resonance site and donation included. |
| """ |
|
|
| import cv2 |
| import numpy as np |
| import soundfile as sf |
| import math |
| import argparse |
| import json |
| from pathlib import Path |
| from datetime import datetime |
| from typing import Dict, Any, Optional |
| import sys |
|
|
| try: |
| from resonance_engine import ResonanceEngine, PRESETS |
| HAS_FULL_ENGINE = True |
| except ImportError: |
| HAS_FULL_ENGINE = False |
| PRESETS = { |
| "pure-light": {"noise_vol": 0.03, "drone_vol": 0.10, "note_vol": 0.14, "glitch_vol": 0.01}, |
| "truth-echo": {"noise_vol": 0.04, "drone_vol": 0.09, "note_vol": 0.12, "glitch_vol": 0.02}, |
| "light-unfold": {"noise_vol": 0.05, "drone_vol": 0.08, "note_vol": 0.13, "glitch_vol": 0.015}, |
| } |
|
|
| __version__ = "0.1.0" |
|
|
| def compute_truth_light_from_image(image_path: str) -> Dict[str, Any]: |
| """Compute Truth × Light proxy from image (contrast × brightness, plus harmony cues).""" |
| img = cv2.imread(str(image_path)) |
| if img is None: |
| raise FileNotFoundError(f"Could not load image: {image_path}") |
|
|
| if len(img.shape) == 2: |
| img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) |
|
|
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
| contrast = float(np.std(gray) / 255.0) |
| brightness = float(np.mean(gray) / 255.0) |
| truth_light = round(contrast * brightness, 4) |
|
|
| |
| edges = cv2.Canny(gray, 50, 150) |
| edge_density = np.sum(edges > 0) / (gray.shape[0] * gray.shape[1]) |
| harmony_cue = round(min(edge_density * 5, 1.0), 4) |
|
|
| return { |
| "source": Path(image_path).name, |
| "truth_light": truth_light, |
| "contrast": round(contrast, 4), |
| "brightness": round(brightness, 4), |
| "harmony_cue": harmony_cue, |
| "edge_density": round(edge_density, 5), |
| } |
|
|
| def ingest_truth_light_from_profile(profile_path: str) -> Dict[str, Any]: |
| """Ingest from JSON profile (Glyph2Resonance, FractalWeaver, or similar).""" |
| with open(profile_path, "r", encoding="utf-8") as f: |
| data = json.load(f) |
|
|
| |
| tl = 0.5 |
| if "LYGO_GLYPH2RESONANCE" in data: |
| lygo = data["LYGO_GLYPH2RESONANCE"]["lygo_mappings"] |
| tl = lygo.get("truth_light", 0.5) |
| phi = lygo.get("phi_resonance", 0.5) |
| harmony = lygo.get("seal_symmetry", 0.5) |
| elif "LYGO_FRACTALWEAVER" in data: |
| lygo = data["LYGO_FRACTALWEAVER"]["lygo_mappings"] |
| tl = lygo.get("truth_light", 0.5) if "truth_light" in lygo else 0.5 |
| phi = lygo.get("self_similarity", 0.5) |
| harmony = lygo.get("recursive_harmony", 0.5) |
| else: |
| |
| tl = data.get("truth_light", data.get("LYGO_PROFILE", {}).get("truth_light", 0.5)) |
| phi = data.get("phi_resonance", 0.5) |
| harmony = data.get("harmony", 0.5) |
|
|
| composite = round((tl + phi + harmony) / 3.0, 4) |
| return { |
| "source": Path(profile_path).name, |
| "truth_light": round(tl, 4), |
| "composite_truth_light": composite, |
| "phi_resonance": round(phi, 4), |
| "harmony": round(harmony, 4), |
| } |
|
|
| def map_to_harmonic_echo_params(truth_light_data: Dict[str, Any], preset: str = "pure-light") -> Dict[str, Any]: |
| """Map Truth × Light to base harmonic echo params + evolution.""" |
| tl = truth_light_data.get("composite_truth_light", truth_light_data.get("truth_light", 0.5)) |
| phi = truth_light_data.get("phi_resonance", 0.5) |
| harm = truth_light_data.get("harmony", 0.5) |
|
|
| base_root = 220 + (tl * 200) |
| base_theta = 4.0 + (phi * 4) |
|
|
| if preset == "pure-light": |
| echo_count = int(4 + tl * 8) |
| delay_base = 0.15 + (1 - tl) * 0.2 |
| decay = 0.85 + tl * 0.1 |
| harmonic_richness = 0.7 + phi * 0.5 |
| evolution_rate = 0.4 + harm * 0.6 |
| elif preset == "truth-echo": |
| echo_count = int(5 + harm * 6) |
| delay_base = 0.2 + (1 - harm) * 0.15 |
| decay = 0.8 + harm * 0.12 |
| harmonic_richness = 0.6 + tl * 0.4 |
| evolution_rate = 0.5 + tl * 0.5 |
| else: |
| echo_count = int(3 + tl * 10) |
| delay_base = 0.1 + (1 - tl) * 0.25 |
| decay = 0.9 + tl * 0.08 |
| harmonic_richness = 0.8 + harm * 0.4 |
| evolution_rate = 0.3 + harm * 0.7 |
|
|
| base_config = { |
| "sr": 44100, |
| "duration": 60.0, |
| "root_freq": base_root, |
| "theta": base_theta, |
| "echo_count": max(3, min(echo_count, 12)), |
| "delay_base": delay_base, |
| "decay": min(decay, 0.98), |
| "harmonic_richness": min(harmonic_richness, 1.0), |
| "evolution_rate": evolution_rate, |
| "noise_vol": 0.02, |
| "drone_vol": 0.06 + tl * 0.04, |
| "note_vol": 0.08 + phi * 0.05, |
| "glitch_vol": 0.01 + (1 - harm) * 0.02, |
| "random_seed": None, |
| "verbose": True, |
| } |
|
|
| lygo_meta = { |
| "integral_truth_light": round(tl, 4), |
| "echo_count": base_config["echo_count"], |
| "harmonic_intervals": [1.0, 1.5, 2.0, 2.5, 3.0][:base_config["echo_count"]-1], |
| "recursive_decay": round(base_config["decay"], 3), |
| "evolution_rate": round(evolution_rate, 2), |
| "suggested_duration": base_config["duration"], |
| "preset_used": preset, |
| } |
|
|
| return base_config, lygo_meta |
|
|
| def synthesize_harmonic_echoes(truth_light_data: Dict, config: Dict, output_wav: str): |
| """Built-in harmonic echo synth with recursive self-similar structure (fallback).""" |
| sr = config["sr"] |
| dur = config.get("duration", 60.0) |
| n = int(sr * dur) |
| audio = np.zeros(n, dtype=np.float32) |
|
|
| root = config["root_freq"] |
| theta = config["theta"] |
| echo_count = config["echo_count"] |
| delay_base = config["delay_base"] |
| decay = config["decay"] |
| rich = config["harmonic_richness"] |
| evo = config["evolution_rate"] |
|
|
| |
| t = np.linspace(0, dur, n, False, dtype=np.float32) |
| drone = np.sin(2 * np.pi * root * t).astype(np.float32) |
| audio += drone * config["drone_vol"] |
|
|
| |
| for e in range(echo_count): |
| interval = 1.0 + (e * (0.5 + rich * 0.3)) |
| delay = delay_base * (1 + e * (0.3 + evo * 0.2)) |
| echo_start = int(delay * sr) |
| if echo_start >= n: |
| break |
|
|
| echo_len = n - echo_start |
| t_echo = np.linspace(0, echo_len / sr, echo_len, False, dtype=np.float32) |
| harm_freq = root * interval |
| echo = np.sin(2 * np.pi * harm_freq * t_echo).astype(np.float32) |
|
|
| |
| amp = (config["note_vol"] * (rich ** e)) * (decay ** (e * 2)) |
| |
| env = np.linspace(0.6, 1.0, echo_len) * np.linspace(1.0, 0.3, echo_len) |
| echo *= amp * env |
|
|
| audio[echo_start:echo_start + echo_len] += echo[:echo_len] |
|
|
| |
| if config["glitch_vol"] > 0: |
| noise = np.random.uniform(-0.5, 0.5, n).astype(np.float32) |
| audio += noise * config["glitch_vol"] * (0.5 + evo * 0.5) |
|
|
| |
| audio = np.tanh(audio * 1.4) / 1.4 |
| peak = np.max(np.abs(audio)) |
| if peak > 0: |
| audio = (audio / peak * 0.96).astype(np.float32) |
|
|
| sf.write(output_wav, audio, sr) |
| return output_wav |
|
|
| def main(): |
| parser = argparse.ArgumentParser( |
| description="LYGO TruthLightEcho — Generate harmonic echo sequences from ∫Truth×Light" |
| ) |
| parser.add_argument("input", nargs="?", help="Image or .json profile from prior tools (Glyph2Resonance, FractalWeaver, etc.)") |
| parser.add_argument("--preset", choices=["pure-light", "truth-echo", "light-unfold"], default="pure-light") |
| parser.add_argument("--seed", type=int, default=None) |
| parser.add_argument("--duration", type=float, default=60.0) |
| parser.add_argument("-o", "--output", default=None) |
| parser.add_argument("--profile", default=None) |
| parser.add_argument("--batch", action="store_true") |
| parser.add_argument("--truth-light", type=float, default=None, help="Manual Truth × Light score (0-1)") |
| args = parser.parse_args() |
|
|
| if not args.input and args.truth_light is None: |
| parser.print_help() |
| return |
|
|
| if args.input: |
| inp = Path(args.input) |
| if inp.suffix.lower() in [".json"]: |
| tl_data = ingest_truth_light_from_profile(str(inp)) |
| else: |
| tl_data = compute_truth_light_from_image(str(inp)) |
| else: |
| tl_data = {"truth_light": args.truth_light, "composite_truth_light": args.truth_light, "source": "manual"} |
|
|
| config, lygo_meta = map_to_harmonic_echo_params(tl_data, args.preset) |
|
|
| if args.seed is not None: |
| config["random_seed"] = args.seed |
| config["duration"] = args.duration |
|
|
| wav_path = args.output or f"truthlightecho_{Path(args.input).stem if args.input else 'manual'}.wav" |
| json_path = args.profile or f"truthlightecho_{Path(args.input).stem if args.input else 'manual'}.truthlight.echo.json" |
|
|
| if HAS_FULL_ENGINE: |
| |
| pass |
| synthesize_harmonic_echoes(tl_data, config, wav_path) |
|
|
| full_profile = { |
| "LYGO_TRUTHLIGHTECHO": { |
| "version": __version__, |
| "source_input": str(args.input) if args.input else "manual", |
| "preset": args.preset, |
| "truth_light_data": tl_data, |
| "audio_config": config, |
| "lygo_mappings": lygo_meta, |
| "generated_at": datetime.now().isoformat(), |
| "reproducible_with_seed": args.seed, |
| } |
| } |
| with open(json_path, "w", encoding="utf-8") as f: |
| json.dump(full_profile, f, indent=2) |
|
|
| print(f"✓ Harmonic echo sequence: {wav_path}") |
| print(f"✓ Profile: {json_path}") |
| print(f" integral={lygo_meta['integral_truth_light']}, echoes={lygo_meta['echo_count']}, recursive_decay={lygo_meta['recursive_decay']}") |
|
|
| |
| try: |
| sys.path.insert(0, str(Path.cwd())) |
| from lyra_brain import LyraThreeBrainMemory |
| brain = LyraThreeBrainMemory(base_dir=Path.cwd(), use_advanced=True) |
| 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']}" |
| nid = brain.grow(summary, source="truthlightecho") |
| print(f" Grown to 3-Brain node: {nid}") |
| except Exception: |
| pass |
|
|
| if __name__ == "__main__": |
| main() |