MusicBee Clouseau

# Documentation

Clouseau is a comprehensive diagnostic and introspection tool for MusicBee plugin developers. Beyond event logging, it provides deep reflection capabilities for exploring assemblies, invoking methods, and inspecting UI internals.

## Overview

Purpose: Clouseau helps you understand MusicBee's plugin API and debug your own plugins through event monitoring and runtime introspection.

- Event Logging: Subscribe to all 40 MusicBee notification types with rich context (30+ fields per event)
- API Interception: Track timing and statistics for 60+ MusicBee API calls with slow call detection
- System Metrics: Monitor CPU, memory, GC pressure, and handle leaks in real-time
- Leak Detection: Track GDI, USER, and Kernel handle trends to identify resource leaks before they crash MusicBee
- Plugin Discovery: Scan and inspect all installed MusicBee plugins
- Assembly Browser: Explore all loaded .NET assemblies, namespaces, and types
- Method Invoker: Call any method via reflection with parameter input and return value display
- Menu Inspector: Walk MusicBee's complete menu hierarchy and invoke items programmatically
- Event Handler Inspector: View all event handlers attached to any WinForms control
- Spy Tab: Deep object inspection showing properties, fields, and runtime values

Scope: Clouseau runs as a MusicBee plugin and only inspects within MusicBee's process. Events are MusicBee notifications only. The Assembly Browser shows assemblies loaded in MusicBee (MusicBee.exe, plugins, .NET framework). System Metrics report MusicBee process stats. It cannot see other applications or system-wide activity outside MusicBee's sandbox.

About

Click to close

Tools Menu

Click to close

Dockable Panel

Click to close

Live Events

Click to close

Dashboard

Click to close

## Notification Subscription

### ReceiveNotificationFlags

Clouseau subscribes to all four notification flags:

    _about.ReceiveNotifications =
        ReceiveNotificationFlags.PlayerEvents |      // 0x1
        ReceiveNotificationFlags.DataStreamEvents |  // 0x2
        ReceiveNotificationFlags.TagEvents |         // 0x4
        ReceiveNotificationFlags.DownloadEvents;     // 0x8

### NotificationType Events (40 Total)

# | Event | Value | Category | Description
--- | --- | --- | --- | ---
1 | `PluginStartup` | 0 | Core | Plugin initialized successfully
2 | `TrackChanged` | 1 | Player | Current track changed
3 | `PlayStateChanged` | 2 | Player | Play/pause/stop state changed
4 | `AutoDjStarted` | 3 | Player | AutoDJ mode started
5 | `AutoDjStopped` | 4 | Player | AutoDJ mode stopped
6 | `VolumeMuteChanged` | 5 | Player | Mute toggled
7 | `VolumeLevelChanged` | 6 | Player | Volume level changed
8 | `NowPlayingListChanged` | 7 | Queue | (Obsolete) Use PlayingTracksChanged
9 | `NowPlayingArtworkReady` | 8 | Player | Artwork loaded for current track
10 | `NowPlayingLyricsReady` | 9 | Player | Lyrics loaded for current track
11 | `TagsChanging` | 10 | Tags | Tags about to be modified
12 | `TagsChanged` | 11 | Tags | Tags were modified
13 | `RatingChanged` | 12 | Tags | Track rating changed
14 | `PlayCountersChanged` | 13 | Tags | Play/skip count updated
15 | `ScreenSaverActivating` | 14 | System | Screen saver about to activate
16 | `RatingChanging` | 15 | Tags | Rating about to change
17 | `TrackChanging` | 16 | Player | Track about to change
18 | `ShutdownStarted` | 17 | Core | MusicBee shutting down
19 | `NowPlayingListEnded` | 18 | Queue | Reached end of queue
20 | `EmbedInPanel` | 19 | UI | Plugin panel embedded
21 | `PlayerRepeatChanged` | 20 | Player | Repeat mode changed
22 | `PlayerShuffleChanged` | 21 | Player | Shuffle toggled
23 | `PlayerEqualiserOnOffChanged` | 22 | Player | Equalizer toggled
24 | `PlayerScrobbleChanged` | 23 | Player | Scrobbling toggled
25 | `ReplayGainChanged` | 24 | Player | ReplayGain mode changed
26 | `FileDeleting` | 25 | Library | File about to be deleted
27 | `FileDeleted` | 26 | Library | File was deleted
28 | `ApplicationWindowChanged` | 27 | UI | Window state changed
29 | `StopAfterCurrentChanged` | 28 | Player | Stop after current toggled
30 | `LibrarySwitched` | 29 | Library | Active library changed
31 | `FileAddedToLibrary` | 30 | Library | New file added to library
32 | `FileAddedToInbox` | 31 | Library | New file added to inbox
33 | `SynchCompleted` | 32 | Sync | Device sync completed
34 | `DownloadCompleted` | 33 | Download | Download finished
35 | `MusicBeeStarted` | 34 | Core | MusicBee fully initialized
36 | `PlayingTracksChanged` | 35 | Queue | Now playing list modified
37 | `PlayingTracksQueueChanged` | 36 | Queue | Queue portion changed
38 | `PlaylistCreated` | 37 | Playlist | New playlist created
39 | `PlaylistUpdated` | 38 | Playlist | Playlist modified
40 | `PlaylistDeleted` | 39 | Playlist | Playlist deleted

## MusicBee API Categories

### 1. MB_* - MusicBee Core

Core MusicBee operations: window handle, panels, menus, background tasks, localization.

- `MB_Trace()` - Write to MusicBee trace log
- `MB_GetWindowHandle()` - Get main window handle
- `MB_AddMenuItem()` - Add menu item
- `MB_AddPanel()` - Add dockable panel
- `MB_CreateBackgroundTask()` - Create background task

### 2. Setting_* - Settings

Access MusicBee settings: storage path, skin, fonts, thresholds.

- `Setting_GetPersistentStoragePath()` - Plugin data folder
- `Setting_GetSkin()` - Current skin name
- `Setting_GetValue()` - Get setting by ID

### 3. Library_* - Library Operations

Query library, get/set tags, manage artwork.

- `Library_QueryFilesEx()` - Query all library files
- `Library_GetFileTag()` - Get metadata tag
- `Library_SetFileTag()` - Set metadata tag
- `Library_GetArtworkEx()` - Get artwork

### 4. Player_* - Player Control

Playback control: play, pause, stop, volume, shuffle, repeat.

- `Player_GetPlayState()` - Current play state
- `Player_GetPosition()` - Playback position (ms)
- `Player_PlayPause()` - Toggle play/pause
- `Player_GetVolume()` - Get volume (0.0-1.0)

### 5. NowPlaying_* - Current Track

Information about the currently playing track.

- `NowPlaying_GetFileUrl()` - Current track path
- `NowPlaying_GetDuration()` - Duration (ms)
- `NowPlaying_GetFileTag()` - Get tag value
- `NowPlaying_GetArtwork()` - Get artwork

### 6. NowPlayingList_* - Queue

Now Playing queue management.

- `NowPlayingList_GetCurrentIndex()` - Current index
- `NowPlayingList_QueueNext()` - Queue as next
- `NowPlayingList_Clear()` - Clear queue

### 7. Playlist_* - Playlists

Playlist management: create, modify, query.

- `Playlist_QueryPlaylists()` - List playlists
- `Playlist_CreatePlaylist()` - Create playlist
- `Playlist_AppendFiles()` - Add files

## Log Entry Structure

    {
      "ts": "2026-01-01 14:32:01.234",
      "level": "INFO",
      "seq": "1234",
      "cat": "Player",
      "event": "TrackChanged",
      "eventId": "1",
      "msg": "Now playing: Dark Side of the Moon - Pink Floyd",
      "src": "C:\\Music\\song.mp3",
      "Timestamp": "2026-01-01T14:32:01.234Z",
      "Artist": "Pink Floyd",
      "Title": "Dark Side of the Moon",
      "Album": "DSOTM",
      "Duration": "234000",
      "Bitrate": "320",
      "Format": "FLAC",
      "Position": "0"
    }

The `events.jsonl` file contains one JSON object per line with ALL context data for easy parsing.

## Log Categories

Category | Events
--- | ---
Core | PluginStartup, ShutdownStarted, MusicBeeStarted
Player | TrackChanged, PlayStateChanged, Volume*, Shuffle, Repeat, etc.
Queue | PlayingTracksChanged, NowPlayingListEnded, etc.
Library | FileAdded*, FileDeleted, LibrarySwitched
Tags | TagsChanged, RatingChanged, PlayCountersChanged
Playlist | PlaylistCreated, PlaylistUpdated, PlaylistDeleted
Sync | SynchCompleted
Download | DownloadCompleted
UI | EmbedInPanel, ApplicationWindowChanged
System | ScreenSaverActivating

## Settings File

Location: `%AppData%\MusicBee\mb_clouseau\settings.json`

    {
      "logLevel": "verbose",
      "enabledCategories": ["Player", "Library", "Tags", "Queue", "Playlist"],
      "fileLogging": true,
      "maxLogSizeMB": 10,
      "autoScroll": true,
      "timestampFormat": "HH:mm:ss.fff",
      "showSourceFile": true,
      "exportFormat": "json"
    }

## Architecture

    mb_clouseau/
    ├── mb_clouseau.csproj       # Project file
    ├── plugin/
    │   ├── Plugin.cs            # Main entry, event routing
    │   ├── MusicBeeInterface.cs # MusicBee API definitions
    │   ├── EventLogger.cs       # Structured logging engine
    │   └── UI/
    │       ├── LogViewerPanel.cs # Real-time log display
    │       └── SettingsDialog.cs # Configuration UI
    ├── README.md
    ├── SPEC.md
    └── CHANGELOG.md

## Building from Source

### Prerequisites

- Visual Studio 2022 or .NET SDK
- .NET Framework 4.8 targeting pack

### Build Commands

    # Using build script
    build.cmd Release

    # Or using dotnet directly
    dotnet build -c Release

Either method works. Output: `publish\mb_clouseau.dll`

## Innovative Features

What makes Clouseau unique for MusicBee plugin development:

### 1. API Call Interception

Hook into MusicBee's API to log all plugin interactions. Every API call is timed and recorded:

- Call Statistics: Count, min/max/average duration per method
- Slow Call Detection: Alerts when calls exceed thresholds (default: warn at 100ms, error at 500ms)
- Anomaly Detection: Detects rapid-fire calls (>100/second) and high error rates (>10%)
- Recent Call History: Last 1,000 API calls with timing data

Enable/Disable: Settings > Introspection tab > "Track API Calls" checkbox, or set `Performance.TrackApiCalls` in settings.json

### 2. CLR/.NET Internals

Deep visibility into the .NET runtime - data developers rarely see:

- GC Collection Counts: Gen 0, Gen 1, Gen 2 (full) collections
- % Time in GC: How much CPU time the garbage collector consumes
- Heap Sizes: Gen 0/1/2 heap and Large Object Heap (LOH) sizes
- GC Pressure Detection: Automatic alerts when GC is working too hard

What is GC Pressure? When your application allocates memory rapidly, the garbage collector must run frequently to reclaim it. High GC pressure means >5 Gen 2 collections per minute or >20% time spent in GC. This causes application pauses and performance issues. Clouseau detects this automatically and logs warnings.

Metrics Tab

Click to close

### 3. Handle Leak Detection

Track Windows handles to find resource leaks before they crash MusicBee:

- GDI Objects: Fonts, brushes, bitmaps, device contexts
- USER Objects: Windows, menus, icons, cursors
- Kernel Handles: Files, registry keys, events, mutexes
- Trend Analysis: 5-second snapshots over 1-minute sliding window
- Leak Source Identification: Reports which handle type is leaking (GDI, USER, or Kernel)

Understanding Leak Warnings: The leak detector uses a 60-second sliding window (12 samples at 5-second intervals). A warning fires when both conditions are met:

1. More than 75% of samples show positive growth (handles or memory increasing)
2. Cumulative increase exceeds threshold (10+ handles/minute or 5+ MB/minute)

#### Why Warnings Appear and Disappear

This is expected behavior, not a bug. Here's how it works:

- Plugin loads: Handles increase consistently (+2, +3, +2 per sample) → Warning fires after 30 seconds
- Plugin stabilizes: Growth stops or GC runs, some samples show negative deltas → <75% positive samples → Warning clears
- Oldest samples age out: After 60 seconds, the initial growth samples drop from the window → Warning stays clear

#### How to Interpret Warnings

Scenario | Meaning | Action
--- | --- | ---
Warning appears once, then clears | Normal startup overhead or temporary allocation | No action needed
Warning appears repeatedly over hours | Likely a real leak | Check metrics log for sustained growth
GDI Objects leaking | Fonts, brushes, bitmaps not disposed | Review Graphics object lifecycle
USER Objects leaking | Windows/controls not destroyed | Review Form/Control disposal
Kernel Handles leaking | Files, registry, events not closed | Review file/stream disposal

Thresholds: Handle leak = 10+ handles gained per minute with sustained trend. Memory leak = 5+ MB gained per minute with sustained trend. Detection requires at least 30 seconds (6 samples) of data.

### 4. Deep State Dumps

Capture complete application state for debugging:

- Player state (current track, position, volume, shuffle, repeat)
- Loaded assemblies and their versions
- Thread information and stack traces
- Memory snapshot with heap statistics
- Registered plugins and their metadata
- Queue contents and playlist state

Create a dump: Dashboard > Diagnostics tab > "Create State Dump" button

Dumps Tab

Click to close

### 5. Plugin Discovery

Scan and inspect all installed MusicBee plugins:

- Plugin name, version, author, type
- File location and load status
- Validation indicators (valid plugin vs. failed to load)
- Error details for plugins that failed to initialize

Plugins Tab

Click to close

### 6. Rich Event Context

Every MusicBee event includes 30+ context fields:

- Track Metadata: Artist, Title, Album, Genre, Year, Rating, BPM, etc.
- Playback State: Position, Duration, Volume, Shuffle, Repeat, Crossfade
- Audio Properties: Bitrate, Sample Rate, Channels, Format, Codec
- Queue Info: Current index, has next/prior tracks
- File Properties: Size, Date Added, Last Played, Play Count

Events Tab

Click to close

## Configuration

### Settings File

Location: `%AppData%\MusicBee\mb_clouseau\settings.json`

### Key Settings

Setting | Default | Description
--- | --- | ---
`Performance.TrackApiCalls` | true | Enable API call interception and timing
`Performance.WarnThresholdMs` | 100 | Warn when API call exceeds this (ms)
`Performance.ErrorThresholdMs` | 500 | Error when API call exceeds this (ms)
`SystemMetrics.Enabled` | true | Collect CPU, memory, disk metrics
`SystemMetrics.CollectionIntervalMs` | 5000 | How often to collect metrics (ms)
`UI.AutoScroll` | true | Auto-scroll log panel to newest entries
`UI.MaxDisplayedEntries` | 1000 | Maximum entries in log viewer
`Advanced.BufferSize` | 10000 | Log entries kept in memory

General

Click to close

Metrics

Click to close

Introspection

Click to close

UI

Click to close

Export

Click to close

Advanced

Click to close

## Advanced Introspection Tools

Clouseau includes powerful introspection tools for deep debugging and exploration of MusicBee internals.

### UI Gallery

Screenshot mode for capturing plugin dialogs and MusicBee windows.

- Auto-Refresh: Periodically capture updated screenshots
- Window Discovery: Lists all open windows and dialogs
- Export: Save screenshots for documentation or bug reports

Access: Dashboard > View menu > UI Gallery

### App Spy Overlay

Transparent overlay that highlights UI elements under your cursor.

- Control Info: Name, type, and bounds of controls
- Click-Through: Overlay doesn't interfere with normal use
- Real-Time: Updates as you move the mouse

Access: Dashboard > View menu > Spy Overlay

### Method Invoker

Browse loaded assemblies and invoke methods via reflection.

- Assembly Browser: Tree view of all loaded assemblies and types
- Method Filtering: Show/hide private methods, search by name
- Parameter Input: UI for entering method parameters with type validation
- Return Values: Display formatted results and execution time
- Static Methods: Invoke without requiring an object instance

Access: Dashboard > Invoke tab > Open button, or View menu > Method Invoker

Invoke Tab

Click to close

Method Invoker

Click to close

### Event Inspector

View and manage event handlers attached to WinForms controls.

- Handler Discovery: List all event handlers on any control
- Handler Details: See method name, target type, and visibility
- Invoke Handlers: Trigger event handlers manually for testing
- Control Tree: Navigate the UI hierarchy to find controls

Access: Dashboard > Handlers tab > Event button

Handlers Tab

Click to close

Event Inspector

Click to close

### Menu Inspector

Walk MusicBee's menu hierarchy and invoke menu items.

- Menu Tree: Complete hierarchy of all menus and submenus
- Item Details: Text, shortcut keys, enabled/checked state
- Invoke Items: Click menu items programmatically
- Search: Find menu items by text

Access: Dashboard > Handlers tab > Menu button

Menu Inspector

Click to close

### Spy Tab (Reflection Explorer)

Deep .NET introspection for exploring objects at runtime.

- Properties & Fields: View all properties and fields with values
- Type Information: See types, access modifiers, attributes
- Event Handlers: List subscribed handlers on any control
- Object Selection: Select controls via overlay for inspection

Access: Dashboard > Spy tab

Spy Tab

Click to close

## Use Cases

### Plugin Development

See exactly what events fire when users interact with MusicBee. Understand the sequence and timing of notifications.

### Debugging

When your plugin isn't receiving expected events, use Clouseau to verify what's actually happening.

### Performance Profiling

Identify slow API calls, memory leaks, and GC pressure that affect user experience.

### API Discovery

Learn what data is available from each API method by watching real calls in action.

### Plugin Conflict Analysis

Watch what other plugins are doing. Debug conflicts by seeing who's changing what and when.

### UI Reverse Engineering

Use the Spy Overlay and Event Inspector to understand how MusicBee's UI is constructed and how controls interact.

## Case Study: Cross-Thread Panel Creation

This case study demonstrates how Clouseau's own logging capabilities helped diagnose a subtle UI bug.

### The Problem

The Clouseau dockable panel appeared blank on MusicBee startup. The panel header (Clear, Dashboard, Settings buttons) was visible, but the ListBox showing events was completely empty. Toggling the panel off and back on would make it work correctly.

### Initial Debugging Attempts

Several fixes were tried without success:

- Deferred control creation with `Application.Idle`
- `VisibleChanged`/`ParentChanged` event handlers with forced refresh
- `HandleCreated` event for timer initialization
- `BeginInvoke` for deferred updates
- Delayed timer refresh (1.5 seconds after startup)
- Switching from owner-draw to standard ListBox mode

None of these worked because they were treating symptoms, not the root cause.

### Using the Logs

The breakthrough came from checking Clouseau's own error log:

    %AppData%\MusicBee\mb_clouseau\logs\errors.log

The log revealed the exact exception:

    2026-01-01 03:38:13|ERROR|Clouseau.UI|Failed to create dockable panel
    System.ArgumentException: Controls created on one thread cannot be parented
    to a control on a different thread.
       at System.Windows.Forms.Control.ControlCollection.Add(Control value)
       at MusicBeePlugin.Plugin.OnDockablePanelCreated(Control panel)

### Root Cause

MusicBee calls `OnDockablePanelCreated` from a background thread during startup, but WinForms controls must be created and parented on the UI thread. The exception was being caught and logged, but the panel appeared blank because no controls were added.

### The Fix

    public int OnDockablePanelCreated(Control panel)
    {
        // Initialize plugin (safe on any thread)
        if (!_initialized) InitializePlugin();

        // Marshal control creation to UI thread if needed
        if (panel.InvokeRequired)
        {
            LogManager.UI.Debug("Marshaling to UI thread");
            panel.Invoke(new Action(() => CreatePanelControls(panel)));
        }
        else
        {
            CreatePanelControls(panel);
        }

        return Convert.ToInt32(250 * dpiScaling);
    }

### Key Takeaways

1. Check the logs first: Clouseau logs to `errors.log`, `events.log`, and `clouseau.log`. The error log captured the exact exception with stack trace.

2. Log file locations:

- `%AppData%\MusicBee\mb_clouseau\logs\errors.log` - Exceptions and errors
- `%AppData%\MusicBee\mb_clouseau\logs\clouseau.log` - General plugin activity
- `%AppData%\MusicBee\mb_clouseau\logs\events.log` - MusicBee notification events

3. Cross-thread exceptions in WinForms: When a panel appears blank or controls don't show, check for thread affinity issues. Use `InvokeRequired` and `Invoke` to marshal to the UI thread.

4. Silent failures: The try/catch was logging the error but the blank panel gave no visual indication. Always check logs when UI doesn't behave as expected.

5. Eat your own dogfood: Using Clouseau to debug Clouseau proved the value of comprehensive logging for plugin development.

MusicBee Clouseau