| Age | Commit message (Collapse) | Author |
|
handoff(Claude): removed full spectrum storage from STFTCache frames;
getFFT() deleted; viewers use squaredAmplitude + 10*log10 directly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- evalBezier: guard dt<=0 to avoid NaN on degenerate curves
- fitBezier: replace nearest-neighbor control points with Catmull-Rom
tangents (Hermite->Bezier), curve now passes through endpoints
- key 'a': toggle mini-spectrum between original and synth FFT
handoff(Claude): bezier fix + synth FFT comparison
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Sort extracted partials by decreasing peak amplitude
- Add Keep% range slider (1-100%) to toolbar
- Viewer draws omitted partials at 50% opacity (live on slider input)
- synthesizeMQ uses only the top-N% partials
handoff(Claude): partial amplitude filtering complete
|
|
- STFTCache: cache squaredAmplitude (re²+im²) per frame, add getSquaredAmplitude(t)
- getMagnitudeDB uses cached sq amp (10*log10, no sqrt)
- detectPeaks uses squaredAmp from cache instead of recomputing FFT
- extractPartials: use cache frames, return {partials, frames}
- Press 'p' to toggle raw peak overlay in viewer
- threshold input: step=any, change event triggers re-extraction
- runExtraction() shared by button and threshold change
handoff(Claude): mq_partial peaks/cache refactor complete
|
|
|
|
Phase 2 - JS Synthesizer:
- Created mq_synth.js with replica oscillator bank
- Bezier curve evaluation (cubic De Casteljau algorithm)
- Replica synthesis: frequency spread, amplitude decay, phase jitter
- PCM buffer generation from extracted MQ partials
- Normalization to prevent clipping
- Key '1' plays synthesized audio, key '2' plays original
- Playback comparison with animated playhead
STFT Cache Optimization:
- Created STFTCache class in fft.js for pre-computed windowed FFT frames
- Clean interface: getFFT(t), getMagnitudeDB(t, freq), setHopSize()
- Pre-computes all frames on WAV load (eliminates redundant FFT calls)
- Dynamic cache update when hop size changes
- Shared across spectrogram, tooltip, and mini-spectrum viewer
- Significant performance improvement
Mini-Spectrum Viewer:
- Bottom-right overlay (200x100) matching spectral_editor style
- Real-time FFT display at playhead or mouse position
- 100-bar visualization with cyan-to-yellow gradient
- Updates during playback or mouse hover
Files:
- tools/mq_editor/mq_synth.js (new)
- tools/mq_editor/fft.js (STFTCache class added)
- tools/mq_editor/index.html (synthesis playback, cache integration)
- tools/mq_editor/viewer.js (cache-based rendering, spectrum viewer)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Phase 1 deliverables complete:
- MQ extraction with improved tracking
- Spectrogram visualization with zoom/scroll
- Original WAV playback with playhead
- Ready for Phase 2 (JS synthesizer)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Tracking improvements:
- Frequency-dependent threshold (5% of freq, min 20 Hz)
- Candidate system requiring 3-frame persistence before birth
- Extended death tolerance (5 frames) for robust trajectories
- Minimum 10-frame length filter for valid partials
- Result: cleaner, less scattered partial trajectories
Audio playback:
- Web Audio API integration for original WAV playback
- Play/Stop buttons with proper state management
- Animated red playhead bar during playback
- Keyboard shortcuts: '2' plays original, '1' reserved for synthesis
Visualization:
- Power law (gamma=0.3) for improved spectrogram contrast
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- Fixed FFT to 1024 bins for 31.25 Hz resolution (better bass analysis)
- Refactored view state to zoom_factor + t_center for cleaner pan/zoom
- Mousewheel scrolls horizontally, shift+mousewheel zooms (respects deltaX/Y)
- Spectrogram bins now fill complete time/freq buckets at all zoom levels
- Extended dB range to -80→0 dB (80 dB) for better high-amplitude granularity
- Added real-time intensity tooltip in dB
- 50% alpha on spectrogram to reduce clutter over partial trajectories
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Implement McAulay-Quatieri sinusoidal analysis tool for audio compression.
New files:
- doc/SPECTRAL_BRUSH_2.md: Complete design doc (MQ algorithm, data format, synthesis, roadmap)
- tools/mq_editor/index.html: Web UI (file loader, params, canvas)
- tools/mq_editor/fft.js: Radix-2 Cooley-Tukey FFT (from spectral_editor)
- tools/mq_editor/mq_extract.js: MQ algorithm (peak detection, tracking, bezier fitting)
- tools/mq_editor/viewer.js: Visualization (spectrogram, partials, zoom, axes)
- tools/mq_editor/README.md: Usage and implementation status
Features:
- Load WAV → extract sinusoidal partials → fit cubic bezier curves
- Time-frequency spectrogram with hot colormap (0-16 kHz)
- Horizontal zoom (mousewheel) around mouse position
- Axis ticks with labels (time: seconds, freq: Hz/kHz)
- Mouse tooltip showing time/frequency coordinates
- Real-time adjustable MQ parameters (FFT size, hop, threshold)
Algorithm:
- STFT with Hann windows (2048 FFT, 512 hop)
- Peak detection with parabolic interpolation
- Birth/death/continuation tracking (50 Hz tolerance)
- Cubic bezier fitting (4 control points per trajectory)
Next: Phase 2 (JS synthesizer for audio preview)
handoff(Claude): MQ editor Phase 1 complete. Ready for synthesis implementation.
|
|
Replace _pad with noise field in UniformsSequenceParams, providing
effects with non-deterministic random values [0..1] updated each frame.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
When starting from a clean tree (where `src/generated/` does not exist), the build would fail with a "file not found" error for `generated/timeline.h`. This was due to an incorrect dependency graph where `gpu.cc` was compiled before its required header was generated.
This commit fixes the issue by making the dependency explicit:
- Modified `tools/seq_compiler.py` to explicitly generate `timeline.h` alongside `timeline.cc`.
- Updated `cmake/DemoCodegen.cmake` to declare both files as `OUTPUT`s of the timeline compilation step.
- Added a direct dependency from the `gpu` library target to the `generate_timeline` custom target in `cmake/DemoLibraries.cmake`.
- Refactored the generated file paths in `DemoCodegen.cmake` into a single `GENERATED_CODE` variable for improved clarity and future-proofing.
|
|
Refactors the timeline code generator (`tools/seq_compiler.py`) to use the `gpu_init_color_attachment` helper function from `src/gpu/gpu.h`.
This change removes platform-specific `#if !defined(DEMO_CROSS_COMPILE_WIN32)` directives from the generated C++ files (`timeline.cc`, `test_timeline.cc`) and centralizes the platform-aware initialization logic within the existing helper function.
The generated code is now cleaner and easier to maintain. Both native macOS and Windows cross-compilation builds are confirmed to be successful.
|
|
This commit fixes several issues that caused the Windows cross-compilation build (`scripts/build_win.sh`) to fail.
The root causes were platform-specific API differences in the wgpu-native library and incorrect dependency tracking in the CMake build system for generated code.
Changes:
- **`tools/seq_compiler.py`**: The timeline generator now wraps `depthSlice` assignments in `#if !defined(DEMO_CROSS_COMPILE_WIN32)` directives to handle API differences in `WGPURenderPassColorAttachment`.
- **`src/gpu/gpu.h`**: The `gpu_init_color_attachment` helper is now platform-aware, using a preprocessor guard for the `depthSlice` member.
- **`src/effects/*.cc`**: All effects are updated to use the new platform-aware helper or have explicit guards for `depthSlice`. Also, replaced `WGPUTexelCopyTextureInfo` with the cross-platform alias `GpuTextureCopyInfo` in `rotating_cube_effect.cc`.
- **`cmake`**: Added `tools/seq_compiler.py` as an explicit dependency to the `generate_timeline` and `generate_test_demo_timeline` custom commands. This ensures that changes to the generator script correctly trigger a rebuild of the generated C++ files.
- **`scripts/build_win.sh`**: Removed the erroneous attempt to build the `seq_compiler.py` script as a native executable.
With these changes, the Windows cross-compilation build now completes successfully.
|
|
Converts all static_cast<>, reinterpret_cast<> to C-style casts
per CODING_STYLE.md guidelines.
- Modified 12 files across gpu, 3d, util, tests, and tools
- All builds passing, 34/34 tests passing
- No functional changes, pure style cleanup
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Effects now accept start/end time parameters and automatically passthrough
when inactive. Implements buffer chain integrity via compile-time validation.
- Effect base class: dispatch_render() checks time bounds, auto-passthroughs
1:1 input/output effects outside [start, end] interval
- seq_compiler.py: validates producer/consumer lifespan constraints for
multi-output effects, adds --validate flag, always validates before codegen
- Updated all 9 effect classes and test fixtures to pass start/end times
- check_all.sh: includes timeline validation step
- Tests: 34/34 passing, demo runs successfully
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
|
|
|
|
- Removed unused v1 shader declarations (13 variables)
- Removed _v2 suffix from active shader names
- Moved shaders.{h,cc} from src/gpu to src/effects
- Updated all includes and build references
- All tests pass (34/34)
handoff(Claude): Cleaned up shader management, tests passing
|
|
Drastically reduce documentation verbosity while retaining essential info.
README.md (152→82 lines):
- Consolidated features into organized sections
- Single concise v2 format example
- Removed redundant explanations
- Quick start section upfront
ROADMAP.md (685→60 lines):
- Completed features: simple bullet list
- Future work: brief descriptions only
- Removed verbose implementation details
- Removed outdated sections
Net reduction: -851 lines (-90%)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Updates all documentation to reflect Sequence V2 format support:
Timeline Editor (tools/timeline_editor/):
- README.md: Updated features list, file format examples with NODE
declarations and arrow syntax, usage instructions for node editing
- ROADMAP.md: Added completed item 1.0 "Sequence V2 Format Support"
Core Documentation (doc/):
- HOWTO.md: Updated timeline example to use v2 arrow syntax, added
NODE/buffer chain features to visual editor description
- SEQUENCE.md: Marked timeline editor graph visualization as completed
All examples now show v2 format:
EFFECT + ClassName input1 input2 -> output1 output2 start end
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Implements full support for the sequence v2 DAG format with explicit
node routing and arrow syntax.
New features:
- timeline-format.js module for parsing/serializing v2 format
- NODE declarations with typed buffers (u8x4_norm, f32x4, etc.)
- Arrow syntax for effect routing: input1 input2 -> output1 output2
- Buffer chain visualization in properties panel and tooltips
- Node editor modal for adding/deleting node declarations
- Validation for undeclared node references (when NODEs explicit)
- Backward compatible with auto-inferred nodes
Files added:
- tools/timeline_editor/timeline-format.js (214 lines)
- tools/timeline_editor/test_format.html (automated tests)
- workspaces/test/timeline_v2_test.seq (test file with NODE declarations)
Files modified:
- tools/timeline_editor/index.html (~40 changes for v2 support)
All success criteria met. Round-trip tested with existing timelines.
handoff(Claude): Timeline editor now fully supports v2 format with
explicit node routing, NODE declarations, and buffer chain visualization.
Parser handles both explicit NODE declarations and auto-inferred nodes.
Validation only runs when explicit NODEs exist. Ready for production use.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- test_demo now uses workspaces/test/{timeline.seq,music.track}
- Removed tools/test_demo.{seq,track} (no longer used)
- Updated docs to reference workspace files
- Changes to workspaces/test/timeline.seq now trigger rebuild
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Prevents compilation errors when multiple sequences share the same name.
Compiler now appends _{index}_Sequence for unique class names.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- Remove #if !defined(DEMO_CROSS_COMPILE_WIN32) guards
- platform.h included transitively handles depthSlice compatibility
- Regenerate timeline.cc with cleaned output
- Tests: 34/34 passing
|
|
- Remove tools/seq_compiler.cc (replaced by seq_compiler.py)
- Remove C++ seq_compiler build target from cmake/DemoTools.cmake
- Update documentation to remove Gantt chart mentions
- Keep seq_compiler.py (active Python compiler)
- All tests passing (34/34)
|
|
Remove END_DEMO keyword from timeline format. Demo duration now
calculated from max effect end time across all sequences. Sort
sequences by start time at compile time for deterministic ordering.
Changes:
- seq_compiler.py: Auto-calculate duration, sort sequences
- seq_compiler.cc: Remove END_DEMO parsing, sort by start time
- workspaces/test/timeline.seq: Remove END_DEMO directive
- Generated timeline.cc: Duration now 40.0f (was hardcoded)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- seq_compiler.py: Calculate beat_phase from beat_time (was hardcoded 0.0f)
- Refactor: Replace CommonPostProcessUniforms with UniformsSequenceParams
- Remove duplicate struct definition in post_process_helper.h
- Update all CNN effects and tests to use unified uniform struct
- Fixes FlashEffect showing solid white instead of flashing to beat
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- FlashEffect: Beat-synchronized white flash using ShaderComposer
- Loads shader from assets (flash.wgsl) with sequence_uniforms include
- Uses pow(1.0 - beat_phase, 4.0) for sharp flash at beat start
- Updated test_demo.seq to use FlashEffect (was HeptagonEffect)
- Added FlashEffect to test suite (test_demo_effects.cc)
- Made cnn_test conditional on main workspace (fixes build error)
- Flash intensity: 1.0 at beat start, fades to 0.0 by beat end
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Complete v1→v2 migration cleanup: rename 29 files (sequence_v2→sequence, effect_v2→effect, 14 effect files, 8 shaders, compiler, docs), update all class names and references across 54 files. Archive v1 timeline. System now uses standard naming with all versioning removed. 30/34 tests passing.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- Port test_effect_base to EffectV2/SequenceV2
- Port test_demo_effects to v2 effects only
- Remove v1 lifecycle helpers from effect_test_helpers
- Fix cnn_test to not depend on cnn_v1_effect.h
- Fix test_sequence_v2_e2e node redeclaration
Known issue: test_sequence_v2_e2e still fails with bind group error
(needs source/sink texture views set)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Phase 4 complete: V1 system removed, v2 fully operational.
Architecture Changes:
- Explicit Node system with typed buffers (u8x4_norm, f32x4, depth24)
- DAG effect routing with multi-input/multi-output support
- Python compiler (seq_compiler_v2.py) with topological sort and ping-pong optimization
- Compile-time node aliasing for framebuffer reuse
V1 Removal (~4KB):
- Deleted effect.h/cc base classes (1.4KB)
- Deleted 19 v1 effect pairs: heptagon, particles, passthrough, gaussian_blur,
solarize, scene1, chroma_aberration, vignette, hybrid_3d, flash_cube,
theme_modulation, fade, flash, circle_mask, rotating_cube, sdf_test,
distort, moving_ellipse, particle_spray (2.7KB)
V2 Effects Ported:
- PassthroughEffectV2, PlaceholderEffectV2
- GaussianBlurEffectV2 (multi-pass with temp nodes)
- HeptagonEffectV2 (scene effect with dummy texture)
- ParticlesEffectV2 (compute + render, format fixed)
- RotatingCubeEffectV2 (3D with depth node)
- Hybrid3DEffectV2 (Renderer3D integration, dummy textures for noise/sky)
Compiler Features:
- DAG validation (cycle detection, connectivity checks)
- Topological sort for execution order
- Ping-pong optimization (aliased node detection)
- Surface-based and encoder-based RenderV2Timeline generation
- init_effect_nodes() automatic generation
Fixes Applied:
- WebGPU binding layout validation (standard v2 post-process layout)
- Surface format mismatch (ctx.format for blit, RGBA8Unorm for framebuffers)
- Depth attachment compatibility (removed forced depth from gpu_create_render_pass)
- Renderer3D texture initialization (created dummy 1x1 white textures)
- ParticlesEffectV2 format (changed from ctx.format to RGBA8Unorm)
- Encoder-based RenderV2Timeline (added missing preprocess() call)
Testing:
- 34/36 tests passing (2 v1-dependent tests disabled)
- demo64k runs successfully (no crashes)
- All seek positions work (--seek 12, --seek 15 validated)
Documentation:
- Updated PROJECT_CONTEXT.md (v2 status, reference to SEQUENCE_v2.md)
- Added completion entry to COMPLETED.md
TODO (Future):
- Port CNN effects to v2
- Implement flatten mode (--flatten code generation)
- Port remaining 10+ effects
- Update HTML timeline editor for v2 (deferred)
handoff(Claude): Sequence v2 migration complete, v1 removed, system operational.
Phase 5 (editor) deferred per user preference.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- Generate InitializeV2Sequences() in timeline.cc
- Generate RenderV2Timeline() for frame rendering
- Add timeline_v2.h interface header
- Call InitializeV2Sequences() in main.cc
- V2 sequences instantiated at startup
- Ready for v1→v2 rendering switch
All 35 tests passing ✅
handoff(Claude): V2 integration ready, next: switch rendering to v2
|
|
- Update cnn_test.cc references
- Update test_demo.cc references (CNNEffect -> CNNv1Effect)
- Fixes build errors from v1 renaming
35/36 tests passing (SilentBackendTest pre-existing failure)
|
|
- Update main workspace to use timeline_v2.seq
- Add SEQ_COMPILER_V2 using Python script (seq_compiler_v2.py)
- Update DemoCodegen to use v2 compiler for main timeline
- Add v1 compatibility stubs (LoadTimeline, GetDemoDuration)
- Demo builds and links successfully
- All tests passing (36/36)
V2 timeline now integrated into build pipeline. Stub functions allow
linking while proper MainSequence v2 integration is pending.
handoff(Claude): V2 timeline integrated, ready for effect ports
|
|
- Remove debug output from seq_compiler_v2.py
- Add get_effect_dag() accessor for testing
- Add e2e test skeleton (shader compatibility pending)
handoff(Claude): v2 foundation complete, 3 phases done
|
|
- Pure Python 3 compiler for v2 timeline syntax
- DAG validation: cycle detection, connectivity, node inference
- Topological sort (Kahn's algorithm)
- Lifetime analysis for optimization
- Ping-pong detection framework (needs refinement)
- Multi-input/multi-output effect routing
- Generates optimized C++ SequenceV2 subclasses
Validated on:
- Simple linear chain (source->temp->sink)
- Complex DAG (deferred render + compose + post)
- Generates correct execution order
Phase 2 complete. Next: Phase 3 effect migration
handoff(Claude): Phase 2 complete, compiler generates valid C++
|
|
Renamed files and classes:
- cnn_effect.{h,cc} → cnn_v1_effect.{h,cc}
- CNNEffect → CNNv1Effect
- CNNEffectParams → CNNv1EffectParams
- CNNLayerParams → CNNv1LayerParams
- CNN_EFFECT.md → CNN_V1_EFFECT.md
Updated all references:
- C++ includes and class usage
- CMake source list
- Timeline (workspaces/main/timeline.seq)
- Test file (test_demo_effects.cc)
- Documentation (CLAUDE.md, PROJECT_CONTEXT.md, READMEs)
Tests: 34/34 passing (100%)
|
|
Move all CNN v2 files to dedicated cnn_v2/ directory to prepare for CNN v3 development. Zero functional changes.
Structure:
- cnn_v2/src/ - C++ effect implementation
- cnn_v2/shaders/ - WGSL shaders (6 files)
- cnn_v2/weights/ - Binary weights (3 files)
- cnn_v2/training/ - Python training scripts (4 files)
- cnn_v2/scripts/ - Shell scripts (train_cnn_v2_full.sh)
- cnn_v2/tools/ - Validation tools (HTML)
- cnn_v2/docs/ - Documentation (4 markdown files)
Changes:
- Update CMake source list to cnn_v2/src/cnn_v2_effect.cc
- Update assets.txt with relative paths to cnn_v2/
- Update includes to ../../cnn_v2/src/cnn_v2_effect.h
- Add PROJECT_ROOT resolution to Python/shell scripts
- Update doc references in HOWTO.md, TODO.md
- Add cnn_v2/README.md
Verification: 34/34 tests passing, demo runs correctly.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Extract common styles from various tool HTML files (timeline, spectral, cnn_v2_test, etc.) into a shared stylesheet.
This reduces code duplication and improves maintainability of the tool frontends.
- Create a new 'tools/common/style.css' to house the shared rules.
- Update all tool HTML files to link to the new stylesheet.
- Remove redundant inline styles from individual HTML files.
|
|
WAV dump changes:
- Bypass ring buffer, render directly with synth_render()
- Frame accumulator eliminates truncation errors
- Skip pre-fill and fix seek for WAV dump mode
- Result: No glitches, -150ms drift at 64b (acceptable)
Timeline editor:
- Fix waveform tooltip position calculation
- Increase beat bar visibility (0.5 opacity)
Cleanup:
- Remove all drift debugging code from audio.cc and tracker.cc
Status: Acceptable for now, further investigation needed.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Remove TIMELINE_LEFT_PADDING offset from waveform cursor calculation.
mouseX is already relative to waveform container, no padding adjustment needed.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- Add timelineContainer reference and capture wheel events with { capture: true }
- Remove redundant wheel handlers from individual sequence/effect elements
- Prevents child elements from interfering with zoom/scroll functionality
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Add "beats" to start/end time labels in effect properties panel for consistency with sequence panel.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- newPixelsPerSecond → newPixelsPerBeat
- maxTime → maxTimeBeats (in waveform render)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
- Add audioBuffer check before showing tooltip/cursor in viewport controller
- Sync variable renames in module files (pixelsPerBeat, audioDurationSeconds)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Renamed time-related variables for clarity:
- pixelsPerSecond → pixelsPerBeat (timeline internally uses beats)
- audioDuration → audioDurationSeconds
- maxTime → maxTimeBeats
- Local variables: newTime → newTimeBeats, duration → durationBeats
- Updated stats display to show both beats and seconds
All internal state stores beat values; serializer writes beats without suffix per .seq format.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Replace repeated 60.0/bpm calculations with precomputed secondsPerBeat
and beatsPerSecond properties. Add computeBPMValues helper and updateBPM
function for consistency. Also prevent wheel events on time markers.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
|
Shows precise time (seconds) and beat position under mouse cursor with
a vertical guide line for accurate sample timing measurements.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|