Core Concepts

This page explains the key concepts and architecture of Logic Plugin Manager.

Architecture Overview

Logic Plugin Manager is structured around three main areas:

  1. Components: Audio Unit plugin bundles and their metadata

  2. Logic Management: High-level interface for plugin discovery and organization

  3. Tags & Categories: Logic Pro’s categorization system

The Library’s Structure

logic_plugin_manager/
├── components/          # Audio Unit components
│   ├── AudioComponent   # Individual plugin representation
│   ├── Component        # .component bundle parser
│   └── AudioUnitType    # Audio Unit type enumeration
├── logic/               # Main management interface
│   ├── Logic            # Primary entry point
│   ├── Plugins          # Plugin collection with search
│   └── SearchResult     # Search result with scoring
└── tags/                # Category and tag management
    ├── Category         # Plugin category management
    ├── MusicApps        # Database interface
    ├── Tagpool          # Plugin count tracking
    ├── Properties       # Category sorting
    └── Tagset           # Plugin tag files

Understanding Audio Components

Audio Component Bundle Structure

macOS Audio Unit plugins are distributed as .component bundles:

PluginName.component/
└── Contents/
    ├── Info.plist       # Metadata and component definitions
    ├── MacOS/
    │   └── PluginName   # Executable binary
    └── Resources/       # UI resources, presets, etc.

The Info.plist file contains an AudioComponents array defining one or more Audio Units within the bundle.

AudioComponent Class

Each Audio Unit is represented by an AudioComponent instance with:

  • Identification: Type, subtype, and manufacturer codes

  • Metadata: Name, description, version

  • Categories: Tags assigned in Logic Pro

  • Tags ID: Unique identifier derived from codes

from logic_plugin_manager import Logic

logic = Logic()
plugin = logic.plugins.get_by_name("Pro-Q 3")

# Access component information
print(f"Full Name: {plugin.full_name}")
print(f"Manufacturer: {plugin.manufacturer}")
print(f"Type Code: {plugin.type_code}")
print(f"Subtype Code: {plugin.subtype_code}")
print(f"Manufacturer Code: {plugin.manufacturer_code}")
print(f"Tags ID: {plugin.tags_id}")

Audio Unit Types

Logic Plugin Manager recognizes five Audio Unit types:

Code

Display Name

Alternative Name

Description

aufx

Audio FX

Effect

Audio effect processors

aumu

Instrument

Music Device

Software instruments and synthesizers

aumf

MIDI-controlled Effects

Music Effect

Effects controlled by MIDI input

aumi

MIDI FX

MIDI Generator

MIDI processors and generators

augn

Generator

Generator

Audio generators

from logic_plugin_manager import AudioUnitType

# Get type by code
instrument_type = AudioUnitType.from_code("aumu")
print(instrument_type.display_name)  # "Instrument"

# Search for types
effect_types = AudioUnitType.search("effect")

The Logic Class

The Logic class is the main entry point for plugin management.

Initialization Process

When you create a Logic instance (with lazy=False, the default):

  1. Loads MusicApps database (tagpool and properties files)

  2. Scans /Library/Audio/Plug-Ins/Components directory

  3. Parses each .component bundle’s Info.plist

  4. Creates AudioComponent instances

  5. Loads tagsets and categories for each plugin

  6. Indexes plugins for fast lookups

from logic_plugin_manager import Logic

# Full initialization
logic = Logic()  # Discovers everything

# Lazy initialization (manual control)
logic = Logic(lazy=True)
logic.discover_plugins()      # When ready to load plugins
logic.discover_categories()   # When ready to load categories

Logic Instance Attributes

logic = Logic()

# Access discovered data
logic.plugins           # Plugins collection
logic.components        # Set of Component bundles
logic.categories        # Dict of category_name -> Category
logic.musicapps         # MusicApps database interface

# Configuration paths
logic.components_path   # Path to Components directory
logic.tags_path         # Path to tags database

Categories & Tags

Hierarchical Structure

Categories in Logic Pro use colon-separated hierarchies:

Effects
Effects:Dynamics
Effects:Dynamics:Compressor
Effects:EQ
Instruments
Instruments:Synth
logic = Logic()

# Navigate hierarchy
dynamics = logic.categories["Effects:Dynamics"]
parent = dynamics.parent  # "Effects" category
child = parent.child("EQ")  # "Effects:EQ" category

# Check properties
print(dynamics.plugin_amount)
print(dynamics.is_root)

Category Operations

logic = Logic()

# Create new category
my_cat = logic.introduce_category("Studio Essentials")

# Manage plugins
plugin = logic.plugins.get_by_name("Pro-Q 3")
plugin.add_to_category(my_cat)
plugin.remove_from_category(my_cat)
plugin.move_to_category(my_cat)  # Removes from all others

# Bulk operations
plugins_set = {plugin1, plugin2, plugin3}
logic.add_plugins_to_category(my_cat, plugins_set)

# Update plugin count
logic.sync_category_plugin_amount(my_cat)

Category Sorting

Categories have a sort order managed by the Properties database:

category = logic.categories["Effects:EQ"]

# Check position
print(category.index)
print(category.is_first)
print(category.is_last)
prev, next = category.neighbors

# Reorder
category.move_up(steps=2)
category.move_down()
category.move_to_top()
category.move_to_bottom()
category.move_before(other_category)
category.move_after(other_category)
category.swap(other_category)

Tagsets

What are Tagsets?

Each AudioComponent has an associated .tagset file stored at:

~/Music/Audio Music Apps/Databases/Tags/<tags_id>.tagset

These XML plist files store:

  • nickname: Custom display name

  • shortname: Abbreviated name for UI

  • tags: Dictionary of category assignments

logic = Logic()
plugin = logic.plugins.get_by_name("Pro-Q 3")

# Access tagset
print(plugin.tagset.nickname)
print(plugin.tagset.shortname)
print(plugin.tagset.tags)  # {"Effects:EQ": "user"}

# Modify tagset
plugin.set_nickname("My Favorite EQ")
plugin.set_shortname("PQ3")

Tag Values

Category tags typically have the value "user" indicating user assignment, but can have other values from Logic Pro’s internal management.

MusicApps Database

Database Files

Logic Pro stores category information in two files:

MusicApps.tagpool

Maps category names to plugin counts:

{
    "Effects": 245,
    "Effects:EQ": 18,
    "Instruments": 89,
    ...
}
MusicApps.properties

Stores category sort order and preferences:

{
    "sorting": ["Effects", "Effects:Dynamics", ...],
    "user_sorted": "property"  # or absent for alphabetical
}

Accessing the Database

logic = Logic()
musicapps = logic.musicapps

# Access tagpool
print(musicapps.tagpool.categories)

# Access properties
print(musicapps.properties.sorting)
print(musicapps.properties.user_sorted)

# Modify database
musicapps.introduce_category("New Category")
musicapps.remove_category("Old Category")

Thread Safety

Logic Plugin Manager is not thread-safe. The library performs file I/O operations on Logic Pro’s database files without locking mechanisms.

Recommendations:

  • Use a single Logic instance per process

  • Avoid concurrent writes to categories or tagsets

  • Reload data after external changes (e.g., Logic Pro modifying categories)

Performance Considerations

Initial Discovery

Loading all plugins can take several seconds depending on the number of installed components. Use lazy=True for faster startup when you don’t need immediate access.

Indexing

The Plugins collection maintains multiple indexes. Reindexing is required if you:

  • Modify plugin properties externally

  • Add plugins after initialization

logic = Logic(lazy=True)
logic.discover_plugins()
# ... modify plugins externally ...
logic.plugins.reindex_all()

Search Performance

  • Exact lookups by indexes are O(1)

  • Fuzzy search with rapidfuzz is O(n) but optimized

  • Use max_results to limit computation for large result sets

Best Practices

  1. Single Instance: Create one Logic instance and reuse it

  2. Error Handling: Wrap operations in try-except blocks for specific exceptions

  3. Lazy Loading: Use for CLI tools or services that don’t need immediate access

  4. Reloading: Call load() methods after external modifications to databases

from logic_plugin_manager import (
    Logic,
    CategoryValidationError,
    PluginLoadError
)

try:
    logic = Logic()
except PluginLoadError as e:
    print(f"Failed to load plugins: {e}")
    # Handle gracefully

# Batch operation example
favorites = {
    logic.plugins.get_by_name(name)
    for name in ["Pro-Q 3", "Serum", "Valhalla VintageVerb"]
    if logic.plugins.get_by_name(name)
}

if favorites:
    fav_category = logic.introduce_category("Favorites")
    logic.add_plugins_to_category(fav_category, favorites)
    logic.sync_category_plugin_amount(fav_category)