Skip to content

Latest commit

 

History

History
315 lines (236 loc) · 18.1 KB

AGENTS.md

File metadata and controls

315 lines (236 loc) · 18.1 KB

AGENTS.md: Developer & AI Agent Guidelines for MusicScope.NET

Welcome to MusicScope.NET. This guide provides architecture context, domain concepts, coding conventions, and developer workflows for AI coding assistants and contributors working on this codebase.


1. Project Overview

  • Repository: MusicScope.NET
  • Goal: Modern, high-precision, open-source cross-platform rewrite of the legendary XiVero MusicScope v2.1.0 audio analyzer and acoustic measurement suite.
  • Target Platforms: Windows (win-x64, win-arm64), macOS (osx-arm64, osx-x64), Linux (linux-x64, linux-arm64).
  • Technology Stack:
    • Runtime: .NET 10 (net10.0), C# 13, Native AOT-ready.
    • UI Framework: AvaloniaUI 12.x (XAML + MVVM via CommunityToolkit.Mvvm).
    • Audio Pipeline: Cross-platform FFmpeg streaming audio decoder (FfmpegAudioDecoder).
    • Network Bridge: Asynchronous TCP socket server on port 8989 (DawSocketServer) for DAW plugin compatibility.
    • Testing: xUnit with real audio test vectors.

2. Solution Architecture

MusicScope.NET/
├── MusicScope.slnx              # Solution file (modern XML solution format)
├── src/
│   ├── MusicScope.Core/         # Pure DSP, FFT, ITU-R BS.1770-4 loudness, True Peak, HW analyzers
│   │   ├── DSP/                 # FFT (Cooley-Tukey), Polyphase FIR, K-Weighting IIR, Window Functions
│   │   ├── Loudness/            # EBU R128 / ITU-R BS.1770-4 meter, gating, LRA, S-Mode histogram
│   │   ├── Levels/              # True Peak oversampling meter, CREST factor, RMS, Peak-to-Loudness Ratio (PLR)
│   │   ├── Stereo/              # Phase correlation [-1.0, +1.0], Mid/Side extraction, Goniometer density
│   │   ├── Hardware/            # THD (1 kHz notch), Jitter (11.025 kHz J-Test), Turntable RPM & Wow/Flutter
│   │   ├── AudioAnalysisEngine.cs # Multi-threaded coordinator processing audio blocks
│   │   └── AudioRealtimeSnapshot.cs # Thread-safe UI update state record
│   ├── MusicScope.Audio/        # Universal FFmpeg streaming audio decoder (WAV, FLAC, DSD/DSF/DFF, ALAC, MP3, etc.)
│   ├── MusicScope.Network/      # TCP Socket Server (Port 8989) streaming 32-bit float audio from DAWs
│   ├── MusicScope.Reporting/    # Export reports: CSV, JSON, and formatted TXT
│   └── MusicScope.Desktop/      # AvaloniaUI MVVM desktop application
│       ├── Controls/            # Custom direct-rendered vector controls (60 FPS Skia canvas)
│       │   ├── SModeMeterControl.cs    # Box 3: S-Mode loudness, L/R peak bars, PLR, LU meter, histogram, LRA
│       │   ├── LevelsBoxControl.cs     # Box 2: Numerical readouts (TPL, RMS, CREST, PLR, M, S, I, LRA)
│       │   ├── FormatBoxControl.cs     # Box 1: Format matrix, sample rate, bit depth, DSD flags
│       │   ├── HistoryDialControl.cs   # Box 4: Polar circular history radar dial
│       │   ├── GoniometerControl.cs    # Box 5: Stereo vector scope and phase correlation bar
│       │   ├── SpectrumGraphControl.cs # Linear/Log FFT frequency spectrum
│       │   └── WaterfallControl.cs     # 2D Spectrogram / Waterfall heatmap
│       ├── ViewModels/          # CommunityToolkit MVVM ViewModels (MainViewModel)
│       └── Views/               # MainWindow.axaml and view code-behinds
├── tests/
│   └── MusicScope.Core.Tests/   # xUnit tests verifying DSP accuracy against original standards
├── OriginalJavaApp/             # Original legacy distribution package
│   └── OriginalJavaApp.zip      # Full legacy XiVero MusicScope distribution archive
└── tools/                       # Decompilation archives, scripts, and references
    ├── src-decompiled.zip       # Complete categorized decompiled Java reference files
    └── organize_and_deobfuscate.py # Symbol mapper and bytecode organizer script

3. Essential Developer Commands

All commands should be executed from the repository root:

Unpack Reference Archives (Required Before Development)

Before continuing development, reverse engineering, or verifying DSP/UI parity against the original application, extract the legacy reference archives:

# 1. Unpack original Java distribution into OriginalJavaApp/
Expand-Archive OriginalJavaApp/OriginalJavaApp.zip -DestinationPath OriginalJavaApp/

# 2. Unpack decompiled Java references into tools/src-decompiled/
Expand-Archive tools/src-decompiled.zip -DestinationPath tools/

Important

Do not commit unzipped files! The extracted contents (OriginalJavaApp/jre/, OriginalJavaApp/lib/, OriginalJavaApp/*.exe, and tools/src-decompiled/) are strictly isolated and ignored by .gitignore to keep the git history clean and compact. Only .zip files should be tracked.

Build Solution

dotnet build MusicScope.slnx

Run Unit Tests

dotnet test --no-build MusicScope.slnx

Run Desktop Application

dotnet run --project src/MusicScope.Desktop/MusicScope.Desktop.csproj

Publish Self-Contained Builds (Single Platform)

Use PowerShell 7 (pwsh) and the packaging helper from the repository root. It publishes Release, self-contained, single-file builds with the original application icon and platform launcher metadata:

The helper enables PublishSingleFile and IncludeNativeLibrariesForSelfExtract, bundles the .NET runtime and native Avalonia libraries, embeds managed debugging symbols, and removes standalone PDB files. Native libraries extract automatically at startup. FFmpeg remains an external dependency and must be available in PATH or beside the executable for audio file decoding.

# Windows (x64 / arm64)
./tools/publish-desktop.ps1 -RuntimeIdentifier win-x64
./tools/publish-desktop.ps1 -RuntimeIdentifier win-arm64

# macOS (Apple Silicon / Intel)
./tools/publish-desktop.ps1 -RuntimeIdentifier osx-arm64
./tools/publish-desktop.ps1 -RuntimeIdentifier osx-x64

# Linux (x64 / arm64)
./tools/publish-desktop.ps1 -RuntimeIdentifier linux-x64
./tools/publish-desktop.ps1 -RuntimeIdentifier linux-arm64

Output defaults to dist/<RuntimeIdentifier>/. Use -OutputDirectory to override it and -Version 1.0.0 to set the assembly and macOS bundle version; the version must have three numeric components without a v prefix. Use fresh output folders for each release to avoid packaging stale files.

  • Windows: output contains one MusicScope.NET.exe, which embeds Assets/MusicScope.ico; the main window uses the same icon.
  • macOS: output contains one MusicScope.NET executable in MusicScope.NET.app/Contents/MacOS/, plus Contents/Resources/MusicScope.icns and Contents/Info.plist. Install the .app in Applications and launch the bundle for the Dock icon and application metadata.
  • Linux: output includes one MusicScope.NET executable plus PNG icons, MusicScope.NET.desktop, and install-desktop-entry.sh. Extract to a permanent location, then run sh ./install-desktop-entry.sh from that directory to install the launcher and icons for the current user. Run it again after moving the application.

Create macOS and Linux archives on Unix to preserve executable permissions. When cross-publishing from Windows, set the executable permission on Unix before packaging (see below). A successful cross-publish does not verify launch behavior or taskbar/Dock icons; check those on each target OS.

Regenerate the Original Application Icon

The checked-in icon assets are generated from the original OriginalJavaApp/MusicScope.exe. Normal builds and publishes do not require Python. To regenerate the assets after extracting the original distribution:

python -m pip install Pillow
python tools/extract_application_icon.py

The extractor preserves the native 16, 32, 48, 64, 128, and 256 pixel Windows icon resources and creates the PNG and ICNS variants in src/MusicScope.Desktop/Assets/. Only the 512 and 1024 pixel Retina variants are upscaled. Commit these generated application assets; keep the extracted legacy distribution ignored.

Creating Cross-Platform GitHub Releases

To build, package, hash, and publish a full multi-platform release for all 6 supported architectures (win-x64, win-arm64, osx-x64, osx-arm64, linux-x64, linux-arm64):

1. Pre-Release Verification & Clean Build Environment

Always verify the full test suite in Release mode before packaging, and start with a clean dist/ directory to prevent stale files:

# Run full test suite in Release mode
dotnet test MusicScope.slnx -c Release

# Remove stale build outputs
Remove-Item -Recurse -Force dist -ErrorAction SilentlyContinue

2. Publish All Target Platforms

Use PowerShell 7 (pwsh) to run the packaging helper for each target architecture:

$ReleaseVersion = "1.2.0"
$Version = "v$ReleaseVersion"
$RIDs = @("win-x64", "win-arm64", "osx-x64", "osx-arm64", "linux-x64", "linux-arm64")
foreach ($rid in $RIDs) {
    ./tools/publish-desktop.ps1 -RuntimeIdentifier $rid -Version $ReleaseVersion
}

3. Verify Single-File Output

The publish helper removes standalone PDBs. Verify that no loose managed/native libraries or debugging symbols remain across any of the six distribution folders, including inside nested macOS bundles:

foreach ($rid in $RIDs) {
    $looseFiles = Get-ChildItem -LiteralPath "dist/$rid" -File -Recurse |
        Where-Object { $_.Name -match '\.(dll|dylib|so(\..*)?|pdb)$' }
    if ($looseFiles) { throw "Unexpected libraries or symbols in dist/$rid; publish to a fresh folder." }
}

4. Package Distribution Archives

Package all six platforms into dist/release/. On a Windows build machine, PowerShell 7 and the built-in Windows tar.exe (bsdtar) can generate all 6 platform archives:

New-Item -ItemType Directory -Force -Path dist/release | Out-Null

# Windows (ZIP containing single-file MusicScope.NET.exe)
Compress-Archive -Path dist/win-x64/* -DestinationPath "dist/release/MusicScope.NET-$Version-win-x64.zip" -Force
Compress-Archive -Path dist/win-arm64/* -DestinationPath "dist/release/MusicScope.NET-$Version-win-arm64.zip" -Force

# macOS (ZIP containing MusicScope.NET.app bundle)
Compress-Archive -Path dist/osx-x64/* -DestinationPath "dist/release/MusicScope.NET-$Version-osx-x64.zip" -Force
Compress-Archive -Path dist/osx-arm64/* -DestinationPath "dist/release/MusicScope.NET-$Version-osx-arm64.zip" -Force

# Linux (TAR.GZ preserving launcher scripts and icon assets)
tar -czf "dist/release/MusicScope.NET-$Version-linux-x64.tar.gz" -C dist/linux-x64 .
tar -czf "dist/release/MusicScope.NET-$Version-linux-arm64.tar.gz" -C dist/linux-arm64 .

(Note: When packaging natively on macOS, ditto -c -k --sequesterRsrc --keepParent dist/$rid/MusicScope.NET.app "dist/release/MusicScope.NET-$Version-$rid.zip" can be used to preserve resource forks. When packaging on Linux, verify chmod +x on the executable before creating the tarball).

5. Generate SHA-256 Checksums

Collect and hash all six archives in dist/release/:

Get-ChildItem -LiteralPath dist/release -File |
    Where-Object { $_.Name -like "MusicScope.NET-$Version-*.zip" -or $_.Name -like "MusicScope.NET-$Version-*.tar.gz" } |
    Sort-Object Name |
    Get-FileHash -Algorithm SHA256 |
    ForEach-Object { "$($_.Hash.ToLowerInvariant())  $(Split-Path $_.Path -Leaf)" } |
    Set-Content -Encoding utf8NoBOM dist/release/SHA256SUMS.txt

6. Compose Release Notes (dist/release/RELEASE_NOTES.md)

Compose structured release notes summarizing changes from merged PRs. Follow this standard format:

# MusicScope.NET v<Version>

Brief overview of the release theme, major improvements, and reference parity accomplishments. Application and bundle versions are **<Version>**.

## Changes

### <Change Area Title> ([PR #<Number>](https://github.com/demoded/MusicScope.NET/pull/<Number>))
Detailed summary of the change:
- Problem or original XiVero parity discrepancy addressed.
- Solution details (DSP mathematical alignment, rendering adjustments, UI thread handling).
- Validation and regression metrics (e.g. dB differences, sample rates tested).
- Mention closed issues (`Fixes #<Number>` or `Resolves issue #<Number>`).

## Downloads and installation

Choose the archive matching your operating system and CPU:

| Platform | x64 | ARM64 |
| --- | --- | --- |
| Windows | [ZIP](https://github.com/demoded/MusicScope.NET/releases/download/<VersionTag>/MusicScope.NET-<VersionTag>-win-x64.zip) | [ZIP](https://github.com/demoded/MusicScope.NET/releases/download/<VersionTag>/MusicScope.NET-<VersionTag>-win-arm64.zip) |
| macOS | [ZIP](https://github.com/demoded/MusicScope.NET/releases/download/<VersionTag>/MusicScope.NET-<VersionTag>-osx-x64.zip) | [ZIP](https://github.com/demoded/MusicScope.NET/releases/download/<VersionTag>/MusicScope.NET-<VersionTag>-osx-arm64.zip) |
| Linux | [TAR.GZ](https://github.com/demoded/MusicScope.NET/releases/download/<VersionTag>/MusicScope.NET-<VersionTag>-linux-x64.tar.gz) | [TAR.GZ](https://github.com/demoded/MusicScope.NET/releases/download/<VersionTag>/MusicScope.NET-<VersionTag>-linux-arm64.tar.gz) |

These packages are self-contained single-file releases including the .NET runtime. **FFmpeg is still required** in `PATH` or beside the executable for audio file decoding.

- **Windows:** extract the ZIP and run `MusicScope.NET.exe`. (If SmartScreen warns on first run, click **More info** -> **Run anyway**, or unblock the archive in File Properties).
- **macOS:** extract the ZIP, copy `MusicScope.NET.app` to Applications, and launch the bundle. The application is unsigned and not notarized (right-click -> **Open**, or go to **System Settings > Privacy & Security > Open Anyway**).
- **Linux:** extract to a permanent location and run `sh ./install-desktop-entry.sh` there to install the launcher and icons for the current user. Run the installer again after moving the application.

Verify downloads against [SHA256SUMS.txt](https://github.com/demoded/MusicScope.NET/releases/download/<VersionTag>/SHA256SUMS.txt).

## Validation

Release solution build completed with zero warnings or errors; all **<Total>** tests passed (<CoreCount> Core, <DesktopCount> Desktop). All six single-file platform packages were verified for absence of loose libraries or standalone PDB symbols, correct bundle structure, and valid checksums.

[Full changelog](https://github.com/demoded/MusicScope.NET/compare/<PreviousTag>...<VersionTag>)

7. Tag and Publish GitHub Release

Create and push the git tag, then publish the release with assets using the GitHub CLI:

# 1. Create and push git tag
git tag -a $Version -m "Release $Version - Cross-Platform Release"
git push origin $Version

# 2. Publish release with assets via GitHub CLI
gh release create $Version `
    dist/release/MusicScope.NET-$Version-win-x64.zip `
    dist/release/MusicScope.NET-$Version-win-arm64.zip `
    dist/release/MusicScope.NET-$Version-osx-x64.zip `
    dist/release/MusicScope.NET-$Version-osx-arm64.zip `
    dist/release/MusicScope.NET-$Version-linux-x64.tar.gz `
    dist/release/MusicScope.NET-$Version-linux-arm64.tar.gz `
    dist/release/SHA256SUMS.txt `
    --title "$Version - <Highlights>" `
    --notes-file dist/release/RELEASE_NOTES.md

4. Architectural Rules & Domain Principles

When modifying or expanding the codebase, always follow these core principles:

1. Parity with the Reference Application

  • The original Java implementation is available in tools/src-decompiled.zip.
  • When implementing or fixing UI controls or DSP algorithms, verify against the decompiled source (e.g., LevelMeterControl.java, LoudnessModule.java, LevelsModule.java, CircleControl.java).
  • Preserve exact mathematical scale transformations, such as the exponential-logarithmic scale mapping: $$\text{norm} = \frac{10^{(dB + 60) / 90} - 1}{10^{63 / 90} - 1}$$

2. Distinction Between Live Metering and Summary Reports

  • Instantaneous Values (InstantPeakLeft/Right, InstantRmsLeft/Right, MomentaryCurrent, ShortTermCurrent, InstantPlr):
    • Active and bouncing during playback/decoding.
    • Reset to $-60\text{ dBFS}$ (or $0.0$ for PLR) when playback/analysis stops or completes.
  • Peak-Hold and Integrated Summary Values (TruePeakLeft/Right, RmsLeft/Right, MomentaryMax, ShortTermMax, IntegratedLoudness, LoudnessRange):
    • Track cumulative statistics across the file.
    • Retained and displayed when playback/analysis finishes (peak hold tick marks, white integrated line, etc.).

3. UI Color Palette & Visual Integrity

  • Backgrounds are pitch black (#000000) or deep carbon (#0A0D10).
  • S-Mode / History: Amber/Orange (#E89A20).
  • LU (Momentary Loudness): Deep Cyan-Blue (#006F96).
  • PLR (Peak-to-Loudness Ratio): Bright Cyan (#55DDFF), drawn vertically between L and R from $0\text{ dB}$ down to $-PLR\text{ dB}$.
  • RMS Indicators: Bright Lime Green (#00FF00).
  • Peak Hold & True Peak: Mint Green (#00E86F), turns Red (#DD0000 or #EE0000) on clip ($\ge 0.05\text{ dB}$).
  • Decaying Peak Hold: Dark Green (#00BB00).

4. Interactive Control Behaviors

  • Custom Avalonia controls override OnPointerPressed for user interactions matching original XiVero shortcuts:
    • In SModeMeterControl: clicking the meter area ($x &lt; 100$) toggles Stereo (L/R) vs Mid/Side (M/S) mode; clicking the distribution area ($x \ge 100$) cycles S-Mode, M-Mode, and TPL.

5. Cross-Platform Compatibility

  • Never rely on platform-specific Windows APIs (GDI+, Win32, DirectX). All rendering must go through Avalonia's cross-platform vector DrawingContext.
  • Audio decoding relies on standard FFmpeg CLI / libraries available across Windows, macOS, and Linux.

5. Coding & Style Conventions

  • Use C# 13 modern features (file-scoped namespaces, pattern matching, collection expressions [...], primary constructors where appropriate).
  • Preserve existing XML documentation comments and docstrings.
  • Keep DSP inner loops allocation-free and optimized with Span<T> and ReadOnlySpan<T>.
  • In Avalonia controls, always register dependency properties with AffectsRender<TControl>(...) to ensure smooth 60 FPS repaints.