Skip to main content

A minimal juce-based plugin host using nanobind

Project description

minihost

Minihost is a headless, JUCE-based audio plugin host that supports VST3, AudioUnit, and LV2 plugins. It provides a C/C++ API for integration and a Python API powered by nanobind.

At a glance

Process an input WAV through a chain of effect plugins and write the result:

import minihost

with (
    minihost.Plugin("/path/to/delay.vst3", sample_rate=48000) as delay,
    minihost.Plugin("/path/to/reverb.vst3", sample_rate=48000) as reverb,
    minihost.PluginChain([delay, reverb]) as chain,
):
    minihost.process_audio_to_file(
        chain, "in.wav", "out.wav",
        tail_seconds=4.0,           # capture reverb tail
    )

process_audio_to_file handles block iteration, latency compensation, sample-rate matching, channel layout, and tail rendering. See the Python API section for lower-level control.

Features

  • Load VST3 plugins (macOS, Windows, Linux)
  • Load AudioUnit plugins (macOS only)
  • Load LV2 plugins (macOS, Windows, Linux)
  • Headless mode (default) - no GUI dependencies, uses JUCE's juce_audio_processors_headless module
  • Plugin chaining - connect multiple plugins in series (synth -> reverb -> limiter)
  • AudioBuffer -- the canonical audio container. Planar float32, JUCE-backed, stdlib-only. Numpy-style 2-axis indexing (buf[ch, frame_slice]), JUCE DSP ops (clear, apply_gain, magnitude, copy), DLPack export so it's accepted directly by Plugin.process / numpy.asarray / PyTorch / etc.
  • numpy is optional. pip install minihost installs no Python runtime dependencies; the AudioBuffer API works without numpy. pip install minihost[numpy] enables numpy-typed APIs (AudioBuffer.as_ndarray(), read_audio(as_=numpy.ndarray), accepting numpy arrays as inputs).
  • High-level offline processing -- process_audio_to_file(plugin_or_chain, "in.wav", "out.wav") collapses block iteration, latency compensation, sample-rate matching, and tail rendering into one call.
  • Audio file I/O via miniaudio + tflac -- read WAV/FLAC/MP3/Vorbis, write WAV (16/24/32-bit) and FLAC (16/24-bit)
  • Sample rate conversion via miniaudio resampler -- minihost.resample() API and minihost resample CLI subcommand
  • Real-time audio playback via miniaudio (cross-platform), with duplex capture mode for effect processing
  • Audio device selection -- enumerate and target specific playback/capture devices (minihost devices CLI, audio_get_playback_devices() / audio_get_capture_devices() API, --playback-device / --capture-device on minihost play)
  • Real-time audio input -- lock-free ring buffer API (write_input()) and duplex capture (capture=True) for routing system audio through effects
  • Real-time MIDI I/O via libremidi (cross-platform)
  • Control surface mapping -- minihost.MidiMapper translates incoming MIDI CCs from a USB control surface (Launch Control / MIDIMix / nanoKONTROL / X-Touch / etc.) onto plugin parameters with optional value-range and curve (linear/exp/log); CLI: minihost play --map "channel:cc:param[:lo:hi[:curve]]" (repeatable) or --map-file PATH for saved JSON mappings.
  • Looped sources for live tweaking -- minihost play --loop-midi PATH loops a MIDI file through the plugin (with All Notes Off between iterations); --loop-audio PATH loops an audio file into the plugin's input ring buffer at real time. Useful for parameter exploration against a repeating pattern.
  • Virtual MIDI ports - create named ports that DAWs can connect to (macOS, Linux)
  • Standalone MIDI input - monitor raw MIDI messages without a plugin (MidiIn class)
  • Batch processing -- glob patterns and directory output for processing multiple files (minihost process -i "*.wav" -o output/)
  • Auto-tail detection -- tail_seconds="auto" monitors output amplitude and stops rendering when reverb/delay tails decay below threshold
  • Process audio with sample-accurate parameter automation
  • Single and double precision processing
  • MIDI input/output support
  • Transport info for tempo-synced plugins
  • State save/restore for presets and per-program state
  • Thread-safe parameter access
  • Change notifications (latency, parameter info, program, non-parameter state) with deferred dispatch via poll_callbacks()
  • Parameter gestures for automation bracketing
  • Bus layout validation and sidechain support
  • Track name/color metadata forwarding to plugins
  • Latency and tail time reporting
  • Parameter access by name -- plugin.find_param("Cutoff"), plugin.get_param_by_name("Cutoff"), plugin.set_param_by_name("Cutoff", 0.5) with case-insensitive lookup
  • Async plugin loading -- minihost.open_async() returns a concurrent.futures.Future for background loading of large sample-library plugins
  • VST3 preset I/O -- read and write .vstpreset files from C (minihost_vstpreset.h), C++, and Python (minihost.vstpreset); minihost presets CLI subcommand exports the current plugin state, optionally after loading a program, state blob, or another .vstpreset

Library structure

minihost ships as two separate static libraries with a one-way dependency: libminihost_audio builds on libminihost, never the reverse.

  • libminihost -- the plugin host core (projects/libminihost/). Loads and runs VST3/AU/LV2 plugins and processes audio blocks you hand it: MIDI in/out, parameters, state save/restore, sample-accurate automation, sidechain, transport, bus layouts, and the routing abstractions (PluginChain, PluginBus, PluginGraph). Depends only on JUCE. C ABI prefix: mh_* (e.g. mh_open, mh_process, mh_chain_*, mh_bus_*, mh_graph_*). Header: minihost.h. Link this alone to load a plugin and feed it your own buffers -- the offline and embedded path, with no device or codec dependencies.

  • libminihost_audio -- the I/O layer around the core (projects/libminihost_audio/). It has no plugin-format knowledge; it gets audio and MIDI in and out of the machine and drives a plugin or chain through its real-time audio callback. Provides live audio device playback/capture (miniaudio), audio file read/write (read WAV/FLAC/MP3/Vorbis; write WAV/FLAC via miniaudio + tflac), MIDI ports (libremidi), and the lock-free ring buffers. Depends on libminihost plus the vendored miniaudio, tflac, and libremidi. C ABI prefix: mh_audio_*. Headers: minihost_audio.h, minihost_audiofile.h, minihost_midi.h. Link this in addition to libminihost when you want real-time devices, file I/O, or MIDI hardware.

In short: libminihost runs the plugin; libminihost_audio connects it to speakers, files, and MIDI hardware. The Python wheel links both.

Requirements

  • CMake 3.20+
  • C++17 compiler
  • JUCE framework (automatically downloaded if not present)
  • Vendored C libraries: miniaudio, tflac, libremidi, midifile (see docs/vendored.md)

Platform-specific

  • macOS: Xcode command line tools

  • Windows: Visual Studio 2019+ or MinGW

  • Linux: Install the following development libraries:

    sudo apt install libasound2-dev libfreetype-dev libfontconfig1-dev \
        libwebkit2gtk-4.1-dev libgtk-3-dev libgl-dev libcurl4-openssl-dev
    

Building

macOS / Linux

# Clone the repository
git clone https://github.com/shakfu/minihost.git
cd minihost

# Build (JUCE will be downloaded automatically)
make

# Or with a custom JUCE path
cmake -B build -DJUCE_PATH=/path/to/JUCE
cmake --build build

# Disable headless mode (enables GUI support)
cmake -B build -DMINIHOST_HEADLESS=OFF
cmake --build build

Windows

# Clone the repository
git clone https://github.com/shakfu/minihost.git
cd minihost

# Download JUCE
python scripts/download_juce.py

# Configure and build
cmake -B build
cmake --build build --config Release

JUCE Setup

JUCE is downloaded automatically by make (macOS/Linux). You can also download it manually:

# Cross-platform (recommended) - works on Windows, macOS, Linux
python scripts/download_juce.py

# Unix only (bash)
./scripts/download_juce.sh

To use a different version or existing installation:

# Download specific version (macOS/Linux)
JUCE_VERSION=8.0.6 python scripts/download_juce.py

# Download specific version (Windows PowerShell)
$env:JUCE_VERSION="8.0.6"; python scripts/download_juce.py

# Or point to existing JUCE
cmake -B build -DJUCE_PATH=/path/to/your/JUCE

Command Line Interface

The minihost command provides a CLI for common plugin operations:

# Install (from source)
uv sync

# Available commands
minihost --help
usage: minihost [-h] [-r SAMPLE_RATE] [-b BLOCK_SIZE]
                {scan,info,params,midi,devices,presets,play,process,resample} ...

Audio plugin hosting CLI

positional arguments:
  {scan,info,params,midi,devices,presets,play,process,resample}
                        Commands
    scan                Scan directory for plugins
    info                Show plugin info
    params              List plugin parameters
    midi                List or monitor MIDI ports
    devices             List audio playback/capture devices
    presets             List factory presets or export .vstpreset files
    play                Play plugin with real-time audio/MIDI
    process             Process audio through plugin (offline)
    resample            Resample audio file to a different sample rate

options:
  -h, --help            show this help message and exit
  -r, --sample-rate SAMPLE_RATE
                        Sample rate in Hz (default: 48000)
  -b, --block-size BLOCK_SIZE
                        Block size in samples (default: 512)

Commands

minihost info - Show plugin info

minihost info /path/to/plugin.vst3          # full info (loads plugin)
minihost info /path/to/plugin.vst3 --probe  # lightweight metadata only
minihost info /path/to/plugin.vst3 --json   # JSON output

By default shows full runtime details (sample rate, channels, latency, buses, presets). Use --probe for fast metadata-only mode without fully loading the plugin.

minihost scan - Scan directory for plugins

minihost scan /Library/Audio/Plug-Ins/VST3/
minihost scan ~/Music/Plugins --json

minihost params - List plugin parameters

minihost params /path/to/plugin.vst3
minihost params /path/to/plugin.vst3 --json

minihost devices - List audio devices

minihost devices                    # list playback and capture devices
minihost devices --json             # JSON output

Use an index or case-insensitive device-name substring with minihost play --playback-device / --capture-device.

minihost presets - List or export factory presets

# List factory presets
minihost presets /path/to/synth.vst3
minihost presets /path/to/synth.vst3 --json

# Export factory preset N as a .vstpreset
minihost presets /path/to/synth.vst3 --program 5 --save preset5.vstpreset

# Round-trip: load a .vstpreset and re-save (preserves class_id)
minihost presets /path/to/synth.vst3 --load-vstpreset in.vstpreset --save out.vstpreset

# Convert a raw state blob to .vstpreset
minihost presets /path/to/synth.vst3 --state state.bin --save out.vstpreset

minihost midi - List or monitor MIDI ports

minihost midi                          # list all MIDI ports
minihost midi --json                   # list as JSON
minihost midi -m 0                     # monitor MIDI input port 0
minihost midi --virtual-midi "Monitor" # create virtual port and monitor

minihost play - Play plugin with real-time audio/MIDI

# Connect to MIDI input port 0
minihost play /path/to/synth.vst3 --midi 0

# Create a virtual MIDI port (macOS/Linux)
minihost play /path/to/synth.vst3 --virtual-midi "My Synth"

# Enable audio input for effect processing (duplex mode)
minihost play /path/to/reverb.vst3 --input
minihost play /path/to/amp-sim.vst3 --input --midi 0  # with MIDI too

# Select specific audio devices (index from `minihost devices` or name substring)
minihost play /path/to/synth.vst3 --playback-device "BlackHole"
minihost play /path/to/effect.vst3 --input --playback-device 0 --capture-device 1
Map a control surface to plugin parameters

--map wires incoming MIDI CCs from a USB control surface (Launch Control, MIDIMix, nanoKONTROL, X-Touch, etc.) onto plugin parameters. When set, MIDI is routed through Python via a MidiMapper; mapped CCs become parameter writes and unmapped events (notes, unmapped CCs) are forwarded to the plugin so notes still play. Format: channel:cc:param[:lo:hi[:curve]]. Curves: linear (default), exp (more resolution at low end), log (more resolution at high end).

# One mapping per --map flag, repeatable
minihost play /path/to/synth.vst3 --midi 0 \
  --map 0:7:Volume \
  --map 0:10:Pan:-1:1 \
  --map 0:74:Cutoff:0:1:exp

For a permanent setup, save the mappings to a JSON file once and load it with --map-file:

{
  "mappings": [
    {"channel": 0, "cc": 7,  "param": "Volume"},
    {"channel": 0, "cc": 10, "param": "Pan", "value_range": [-1.0, 1.0]},
    {"channel": 0, "cc": 74, "param": "Cutoff", "curve": "exp"}
  ]
}
minihost play /path/to/synth.vst3 --midi 0 \
  --map-file ~/.config/minihost/launch_control.json

--map and --map-file are combinable -- the file loads first, CLI args append. Required JSON fields per entry: channel, cc, param. Optional: value_range (default [0.0, 1.0]), curve (default "linear").

Loop a MIDI or audio file as the source

--loop-midi loops a MIDI file into a synth (or any plugin that accepts MIDI), useful for live-tweaking parameters against a repeating pattern. A Python thread schedules events at wall-clock-correct times; All Notes Off is sent on every channel between iterations to silence sustained notes.

# Loop a MIDI pattern through a synth while live-tweaking knobs
minihost play /path/to/synth.vst3 \
  --midi 0 \
  --map 0:74:Cutoff:0:1:exp \
  --loop-midi tests/_wav/test_pattern.mid

--loop-audio loops an audio file as the plugin's input, useful for testing effects against a known source without needing live audio. The ring buffer is auto-enabled; the file is resampled to the device rate if needed. Mutually exclusive with --input.

# Loop a guitar take into a reverb while turning the mix knob
minihost play /path/to/reverb.vst3 \
  --midi 0 \
  --map 0:7:Mix \
  --loop-audio guitar_dry.wav

Both loop flags can run alongside live MIDI input (the file's events and your live notes are merged into the plugin).

minihost process - Process audio/MIDI offline

# Process audio through effect
minihost process /path/to/effect.vst3 -i input.wav -o output.wav

# With parameter control
minihost process /path/to/effect.vst3 -i input.wav -o output.wav --param "Mix:0.5"

# Render MIDI through synth
minihost process /path/to/synth.vst3 -m song.mid -o output.wav --tail 3.0

# With preset and bit depth
minihost process /path/to/synth.vst3 -m song.mid -o output.wav --preset 5 --bit-depth 16

# Sidechain processing (second -i is sidechain)
minihost process /path/to/compressor.vst3 -i main.wav -i sidechain.wav -o output.wav

# Batch processing (glob input, directory output)
minihost process /path/to/reverb.vst3 -i "drums/*.wav" -o processed/
minihost process /path/to/effect.vst3 -i "*.wav" -o output/ -y  # overwrite existing

# Mixed sample rates are automatically resampled (use --no-resample to error instead)
minihost process /path/to/effect.vst3 -i 44100hz.wav -i 48000hz_sidechain.wav -o out.wav

minihost resample - Resample audio files

minihost resample input.wav -o output.wav -r 48000
minihost resample input.wav -o output.wav -r 44100 --bit-depth 16
minihost resample input.wav -o output.wav -r 96000 -y  # overwrite

Global Options

Option Description
-r, --sample-rate Sample rate in Hz (default: 48000)
-b, --block-size Block size in samples (default: 512)

Python API

Install:

pip install minihost              # AudioBuffer-only API; no numpy required
pip install minihost[numpy]       # adds numpy-typed return values + numpy input acceptance

The default audio container is minihost.AudioBuffer (planar float32, JUCE-backed, stdlib-only). It supports DLPack so any C extension that takes a 2D float32 c-contiguous buffer (including all of minihost's process methods) accepts it directly. Numpy is fully supported when installed -- pass as_=numpy.ndarray to receive numpy arrays from read_audio / render_midi, or call .as_ndarray() on any AudioBuffer for a zero-copy numpy view.

Quick start: process a WAV file through a chain

import minihost

with (
    minihost.Plugin("/path/to/delay.vst3", sample_rate=48000) as delay,
    minihost.Plugin("/path/to/reverb.vst3", sample_rate=48000) as reverb,
    minihost.PluginChain([delay, reverb]) as chain,
):
    minihost.process_audio_to_file(
        chain, "in.wav", "out.wav",
        tail_seconds=4.0,           # capture reverb tail
    )

process_audio_to_file handles block iteration, latency compensation, sample-rate matching (input is auto-resampled to the plugin's rate), mono-to-stereo channel duplication, and tail rendering. For in-memory data use process_audio(plugin_or_chain, audio, tail_seconds=...), which returns an AudioBuffer.

Lower-level processing

import minihost

plugin = minihost.Plugin("/path/to/plugin.vst3", sample_rate=48000)

# AudioBuffer is the default container. process accepts it directly via DLPack.
input_audio = minihost.AudioBuffer(2, 512)
output_audio = minihost.AudioBuffer(2, 512)
plugin.process(input_audio, output_audio)

# Numpy users can mix and match -- both accepted as inputs:
import numpy as np                                       # requires minihost[numpy]
input_np = np.zeros((2, 512), dtype=np.float32)
plugin.process(input_np, output_audio)                   # numpy in -> AudioBuffer out
output_np = output_audio.as_ndarray()                    # zero-copy numpy view

Parameter Access by Name

import minihost

plugin = minihost.Plugin("/path/to/synth.vst3", sample_rate=48000)

# Find parameter index by name (case-insensitive)
idx = plugin.find_param("Cutoff")

# Get/set by name directly
value = plugin.get_param_by_name("Cutoff")
plugin.set_param_by_name("Cutoff", 0.7)
plugin.set_param_by_name("resonance", 0.4)  # case-insensitive

# Index-based API remains available for hot paths
plugin.set_param(idx, 0.5)

Async Plugin Loading

import minihost

# Load a heavy plugin in the background
future = minihost.open_async("/path/to/heavy_sampler.vst3", sample_rate=48000)

# Do other work while plugin loads...

# Block until ready
plugin = future.result()
print(f"Loaded: {plugin.num_params} params")

Audio Device Enumeration and Selection

import minihost

# List available audio devices
for dev in minihost.audio_get_playback_devices():
    print(f"[{dev['index']}] {dev['name']}{' *' if dev['is_default'] else ''}")

for dev in minihost.audio_get_capture_devices():
    print(f"[{dev['index']}] {dev['name']}{' *' if dev['is_default'] else ''}")

# Target a specific playback device (e.g., for routing to a loopback driver)
plugin = minihost.Plugin("/path/to/synth.vst3", sample_rate=48000)
with minihost.AudioDevice(plugin, playback_device_index=2) as audio:
    audio.send_midi(0x90, 60, 100)

# Duplex mode with explicit capture + playback devices
with minihost.AudioDevice(plugin, capture=True,
                          capture_device_index=1,
                          playback_device_index=0) as audio:
    pass

Pass -1 (the default) to use the system default device.

Real-time Audio Playback

import minihost
import time

plugin = minihost.Plugin("/path/to/synth.vst3", sample_rate=48000)

# Use as context manager for automatic start/stop
with minihost.AudioDevice(plugin) as audio:
    # Plugin is now producing audio through speakers
    # Send MIDI programmatically
    audio.send_midi(0x90, 60, 100)  # Note on: C4, velocity 100
    time.sleep(1)
    audio.send_midi(0x80, 60, 0)    # Note off
    time.sleep(0.5)

# Or manual control
audio = minihost.AudioDevice(plugin)
audio.start()
audio.send_midi(0x90, 64, 80)  # E4 note on
time.sleep(0.5)
audio.send_midi(0x80, 64, 0)   # E4 note off
audio.stop()

Real-time Audio Input (Effect Processing)

Route system audio through an effect plugin using duplex mode or the ring buffer API:

import minihost
import time

plugin = minihost.Plugin("/path/to/reverb.vst3", sample_rate=48000)

# Option 1: Duplex mode (system audio capture -> plugin -> speakers)
with minihost.AudioDevice(plugin, capture=True) as audio:
    print("Processing system audio through effect... Ctrl+C to stop")
    time.sleep(10)

# Option 2: Ring buffer (push audio from Python).
# AudioBuffer slicing returns a new AudioBuffer; write_input accepts it
# directly via DLPack -- no numpy required.
audio = minihost.AudioDevice(plugin)
audio.enable_input()  # ~0.5s ring buffer by default
audio.start()

data, sr = minihost.read_audio("guitar.wav")
block_size = 512
for i in range(0, data.frames, block_size):
    chunk = data[:, i:i+block_size]
    audio.write_input(chunk)
    time.sleep(block_size / sr * 0.9)  # pace to real time

audio.stop()
audio.disable_input()

Real-time MIDI I/O

import minihost

# Enumerate available MIDI ports
inputs = minihost.midi_get_input_ports()
outputs = minihost.midi_get_output_ports()
print(f"MIDI Inputs: {inputs}")
print(f"MIDI Outputs: {outputs}")

# Connect MIDI when creating AudioDevice
with minihost.AudioDevice(plugin, midi_input_port=0) as audio:
    # MIDI from port 0 is now routed to the plugin
    pass

# Or connect dynamically
audio = minihost.AudioDevice(plugin)
audio.connect_midi_input(0)
audio.start()
# ...
audio.disconnect_midi_input()
audio.stop()

# Create virtual MIDI ports (appear in system MIDI, DAWs can connect)
audio = minihost.AudioDevice(plugin)
audio.create_virtual_midi_input("minihost Input")
audio.create_virtual_midi_output("minihost Output")
audio.start()
# Other apps can now send MIDI to "minihost Input"
# and receive MIDI from "minihost Output"

Standalone MIDI Input

Monitor MIDI messages without loading a plugin:

import minihost

def on_midi(data: bytes):
    status = data[0]
    if status & 0xF0 == 0x90 and data[2] > 0:
        print(f"Note On: {data[1]} vel={data[2]}")

# Open hardware MIDI port
with minihost.MidiIn.open(0, on_midi) as midi_in:
    input("Press Enter to stop...\n")

# Or create a virtual MIDI port
with minihost.MidiIn.open_virtual("My Monitor", on_midi) as midi_in:
    input("Press Enter to stop...\n")

Audio File I/O

import minihost

# Read audio files (WAV, FLAC, MP3, Vorbis).
# Default container is AudioBuffer (planar float32, no numpy required).
data, sample_rate = minihost.read_audio("input.wav")
# data is an AudioBuffer of shape (channels, samples)

# Pass as_=numpy.ndarray to get a numpy array instead (requires minihost[numpy]).
import numpy as np
data_np, sample_rate = minihost.read_audio("input.wav", as_=np.ndarray)

# write_audio accepts AudioBuffer, numpy ndarray, or any DLPack/buffer-protocol producer.
minihost.write_audio("output.wav", data, sample_rate, bit_depth=24)   # WAV (16/24/32-bit)
minihost.write_audio("output.flac", data, sample_rate, bit_depth=24)  # FLAC (16/24-bit)

# Get file info without decoding
info = minihost.get_audio_info("song.wav")
print(f"{info['channels']}ch, {info['sample_rate']}Hz, {info['duration']:.2f}s")

Sample Rate Conversion

import minihost

# Works on AudioBuffer (default), numpy ndarray, or any 2D float32
# c-contig buffer-protocol producer. Return type matches the input type
# (AudioBuffer in -> AudioBuffer out; numpy in -> numpy out).
data, sr = minihost.read_audio("input_44100.wav")  # AudioBuffer @ 44.1kHz
resampled = minihost.resample(data, 44100, 48000)   # -> 48kHz AudioBuffer
minihost.write_audio("output_48000.wav", resampled, 48000)

MIDI File Read/Write

import minihost

# Create a new MIDI file
mf = minihost.MidiFile()
mf.ticks_per_quarter = 480

# Add events
mf.add_tempo(0, 0, 120.0)  # 120 BPM at tick 0
mf.add_note_on(0, 0, 0, 60, 100)    # C4 note on at tick 0
mf.add_note_off(0, 480, 0, 60, 0)   # C4 note off at tick 480

# Save to file
mf.save("output.mid")

# Load existing MIDI file
mf2 = minihost.MidiFile()
mf2.load("input.mid")

# Read events
events = mf2.get_events(0)  # Get events from track 0
for event in events:
    if event['type'] == 'note_on':
        print(f"Note {event['pitch']} vel {event['velocity']} at {event['seconds']:.2f}s")

MIDI File Rendering

Render MIDI files through plugins to produce audio output. Returns AudioBuffer by default; pass as_=numpy.ndarray for numpy:

import minihost

plugin = minihost.Plugin("/path/to/synth.vst3", sample_rate=48000)

# Render to AudioBuffer (default)
audio = minihost.render_midi(plugin, "song.mid")
print(f"Rendered {audio.frames / 48000:.2f} seconds of audio")

# Numpy variant
import numpy as np
audio_np = minihost.render_midi(plugin, "song.mid", as_=np.ndarray)

# Render directly to WAV file (returns frame count)
samples = minihost.render_midi_to_file(plugin, "song.mid", "output.wav", bit_depth=24)

# Stream blocks for large files or real-time processing.
# Each yielded block is an AudioBuffer; pass as_=numpy.ndarray to yield numpy instead.
for block in minihost.render_midi_stream(plugin, "song.mid", block_size=512):
    # block.shape == (channels, n) where n <= block_size
    pass

# Auto-detect reverb/delay tail (stops when output decays below -80 dB)
audio = minihost.render_midi(plugin, "song.mid", tail_seconds="auto")

# Custom threshold (-40 dB) and max tail (10s safety cap)
audio = minihost.render_midi(plugin, "song.mid",
                             tail_seconds="auto", tail_threshold=1e-2, max_tail_seconds=10)

# Fine-grained control with MidiRenderer class
renderer = minihost.MidiRenderer(plugin, "song.mid")
print(f"Duration: {renderer.duration_seconds:.2f}s")

while not renderer.is_finished:
    block = renderer.render_block()   # returns AudioBuffer or None
    print(f"Progress: {renderer.progress:.1%}")

Plugin Chaining

Chain multiple plugins together for serial processing:

import minihost
import time

# Load plugins (all must have same sample rate)
synth = minihost.Plugin("/path/to/synth.vst3", sample_rate=48000)
reverb = minihost.Plugin("/path/to/reverb.vst3", sample_rate=48000)
limiter = minihost.Plugin("/path/to/limiter.vst3", sample_rate=48000)

# Create chain
chain = minihost.PluginChain([synth, reverb, limiter])
print(f"Total latency: {chain.latency_samples} samples")
print(f"Tail length: {chain.tail_seconds:.2f} seconds")

# Real-time playback through chain
with minihost.AudioDevice(chain) as audio:
    audio.send_midi(0x90, 60, 100)  # Note on to synth
    time.sleep(2)
    audio.send_midi(0x80, 60, 0)    # Note off
    time.sleep(1)  # Let reverb tail fade

# Offline processing -- AudioBuffer is the default container
input_audio = minihost.AudioBuffer(2, 512)
output_audio = minihost.AudioBuffer(2, 512)
chain.process(input_audio, output_audio)

# Process with MIDI (MIDI goes to first plugin)
midi_events = [(0, 0x90, 60, 100)]
chain.process_midi(input_audio, output_audio, midi_events)

# Sample-accurate automation across chain
# param_changes: (sample_offset, plugin_index, param_index, value)
param_changes = [
    (0, 1, 0, 0.3),    # Set reverb param 0 at sample 0
    (256, 1, 0, 0.6),  # Change reverb param 0 at sample 256
    (0, 2, 0, 0.8),    # Set limiter param 0 at sample 0
]
chain.process_auto(input_audio, output_audio, midi_events, param_changes)

# Render MIDI file through chain
audio = minihost.render_midi(chain, "song.mid")        # -> AudioBuffer
minihost.render_midi_to_file(chain, "song.mid", "output.wav")

# File-to-file processing through the chain (handles tail, latency, resample)
minihost.process_audio_to_file(chain, "input.wav", "output.wav", tail_seconds=4.0)

# Access individual plugins in chain
for i in range(chain.num_plugins):
    plugin = chain.get_plugin(i)
    print(f"Plugin {i}: {plugin.num_params} params")

Parallel routing (PluginBus)

PluginChain is series; PluginBus is parallel. A bus fans the same input to N branches (each a PluginChain) and sums their outputs with a per-branch gain -- parallel compression, dry-bus + reverb-send, multi-band processing. With process_midi, the same MIDI is delivered to every branch, which is the idiomatic way to layer one part across several instruments:

import minihost

# Three synths layered under one MIDI part, summed to stereo.
a = minihost.PluginChain([minihost.Plugin("/path/to/saw.vst3", sample_rate=48000)])
b = minihost.PluginChain([minihost.Plugin("/path/to/sub.vst3", sample_rate=48000)])
c = minihost.PluginChain([minihost.Plugin("/path/to/pad.vst3", sample_rate=48000)])

bus = minihost.PluginBus(2, 2, max_block_size=512, sample_rate=48000.0)
bus.add_branch(a, gain=1.0)
bus.add_branch(b, gain=0.7)
bus.add_branch(c, gain=0.5)

silence = minihost.AudioBuffer(2, 512)   # synths ignore audio input
out = minihost.AudioBuffer(2, 512)
note_on = [(0, 0x90, 60, 100)]           # C4 reaches ALL three synths
bus.process_midi(silence, out, note_on)

A complete, runnable version (block loop, chord, WAV output) is in examples/parallel_bus.py.

For arbitrary node-to-node topologies (multiple inputs/outputs, MIDI processors, channel pick/merge), use PluginGraph -- the general DAG executor that also backs project files (minihost.load_project). Branch MIDI output is not collected by the bus; reach for PluginGraph if you need that.

VST3 Presets

Read, load, and write Steinberg .vstpreset files:

import minihost
from minihost import vstpreset

plugin = minihost.Plugin("/path/to/synth.vst3")

# Read a .vstpreset into raw chunks
preset = vstpreset.read_vstpreset("patch.vstpreset")
print(preset.class_id, len(preset.component_state or b""))

# Load into a plugin (calls plugin.set_state under the hood)
vstpreset.load_vstpreset("patch.vstpreset", plugin)

# Save the plugin's current state to a .vstpreset.
# class_id defaults to the FUID auto-detected from the plugin bundle's
# moduleinfo.json (requires VST3 SDK 3.7.5+, which all modern plugins ship).
vstpreset.save_vstpreset("out.vstpreset", plugin)

# Or pass class_id explicitly (e.g., for legacy plugins without moduleinfo.json):
vstpreset.save_vstpreset("out.vstpreset", plugin,
                         class_id="ABCDEF0123456789ABCDEF0123456789")

# Read just the class ID from a bundle without instantiating the plugin
fuid = vstpreset.read_class_id_from_bundle("/path/to/synth.vst3")
print(fuid)  # e.g., "ABCDEF0123456789ABCDEF0123456789"

# Or write raw chunks you already have
vstpreset.write_vstpreset("out.vstpreset",
                          class_id=fuid,
                          component_state=plugin.get_state())

C API Usage

#include "minihost.h"

// Load a plugin
char err[256];
MH_Plugin* plugin = mh_open("/path/to/plugin.vst3",
                            48000.0,  // sample rate
                            512,      // max block size
                            2, 2,     // in/out channels
                            err, sizeof(err));

// Process audio
float* inputs[2] = { in_left, in_right };
float* outputs[2] = { out_left, out_right };
mh_process(plugin, inputs, outputs, 512);

// Process with MIDI
MH_MidiEvent midi[] = {
    { 0, 0x90, 60, 100 },   // Note on at sample 0
    { 256, 0x80, 60, 0 }    // Note off at sample 256
};
mh_process_midi(plugin, inputs, outputs, 512, midi, 2);

// Parameter control
int num_params = mh_get_num_params(plugin);
float value = mh_get_param(plugin, 0);
mh_set_param(plugin, 0, 0.5f);

// State save/restore
int size = mh_get_state_size(plugin);
void* state = malloc(size);
mh_get_state(plugin, state, size);
mh_set_state(plugin, state, size);

// Cleanup
mh_close(plugin);

Real-time Audio Playback

#include "minihost_audio.h"

// Enumerate and select a playback device (optional)
MH_AudioDeviceInfo devices[32];
int n = mh_audio_enumerate_playback_devices(devices, 32);
for (int i = 0; i < n; i++) {
    printf("[%d]%s %s\n", i, devices[i].is_default ? "*" : " ", devices[i].name);
}

// Open audio device for real-time playback
MH_AudioConfig config = {
    .sample_rate = 48000,
    .buffer_frames = 512,
    .playback_device_index = -1,  // -1 = system default
    .capture_device_index = -1,
};
MH_AudioDevice* audio = mh_audio_open(plugin, &config, err, sizeof(err));

// Start playback
mh_audio_start(audio);

// Plugin is now producing audio through speakers
// Send MIDI, adjust parameters, etc.

// Stop and cleanup
mh_audio_stop(audio);
mh_audio_close(audio);
mh_close(plugin);

Real-time MIDI I/O

#include "minihost_midi.h"

// Enumerate available MIDI ports
int num_inputs = mh_midi_get_num_inputs();
int num_outputs = mh_midi_get_num_outputs();

for (int i = 0; i < num_inputs; i++) {
    char name[256];
    mh_midi_get_input_name(i, name, sizeof(name));
    printf("MIDI Input %d: %s\n", i, name);
}

// Connect MIDI to audio device
MH_AudioConfig config = {
    .sample_rate = 48000,
    .midi_input_port = 0,   // Connect to first MIDI input
    .midi_output_port = -1  // No MIDI output
};
MH_AudioDevice* audio = mh_audio_open(plugin, &config, err, sizeof(err));

// Or connect/disconnect dynamically
mh_audio_connect_midi_input(audio, 1);
mh_audio_disconnect_midi_input(audio);

// Create virtual MIDI ports (appear in system MIDI, DAWs can connect)
mh_audio_create_virtual_midi_input(audio, "minihost Input");
mh_audio_create_virtual_midi_output(audio, "minihost Output");

Plugin Chaining

Chain multiple plugins together for processing (e.g., synth -> reverb -> limiter):

#include "minihost_chain.h"

// Load plugins
MH_Plugin* synth = mh_open("/path/to/synth.vst3", 48000, 512, 0, 2, err, sizeof(err));
MH_Plugin* reverb = mh_open("/path/to/reverb.vst3", 48000, 512, 2, 2, err, sizeof(err));
MH_Plugin* limiter = mh_open("/path/to/limiter.vst3", 48000, 512, 2, 2, err, sizeof(err));

// Create chain (all plugins must have same sample rate)
MH_Plugin* plugins[] = { synth, reverb, limiter };
MH_PluginChain* chain = mh_chain_create(plugins, 3, err, sizeof(err));

// Get combined latency
int latency = mh_chain_get_latency_samples(chain);

// Process audio through chain
float* inputs[2] = { in_left, in_right };
float* outputs[2] = { out_left, out_right };
mh_chain_process(chain, inputs, outputs, 512);

// Process with MIDI (MIDI goes to first plugin only)
MH_MidiEvent midi[] = { { 0, 0x90, 60, 100 } };
mh_chain_process_midi_io(chain, inputs, outputs, 512, midi, 1, NULL, 0, NULL);

// Sample-accurate automation across chain
MH_ChainParamChange changes[] = {
    { .sample_offset = 0,   .plugin_index = 1, .param_index = 0, .value = 0.3f },
    { .sample_offset = 256, .plugin_index = 1, .param_index = 0, .value = 0.6f },
};
mh_chain_process_auto(chain, inputs, outputs, 512,
                       NULL, 0, NULL, 0, NULL, changes, 2);

// Real-time playback through chain
MH_AudioConfig config = { .sample_rate = 48000, .buffer_frames = 512 };
MH_AudioDevice* audio = mh_audio_open_chain(chain, &config, err, sizeof(err));
mh_audio_start(audio);
// ...
mh_audio_stop(audio);
mh_audio_close(audio);

// Cleanup
mh_chain_close(chain);  // Does not close individual plugins
mh_close(synth);
mh_close(reverb);
mh_close(limiter);

Audio File I/O

Read and write audio files without external dependencies:

#include "minihost_audiofile.h"

// Read any supported format (WAV, FLAC, MP3, Vorbis)
char err[1024];
MH_AudioData* audio = mh_audio_read("input.flac", err, sizeof(err));
if (audio) {
    printf("Channels: %u, Frames: %u, Rate: %u\n",
           audio->channels, audio->frames, audio->sample_rate);
    // audio->data is interleaved float32
    mh_audio_data_free(audio);
}

// Write audio file (format selected by extension)
mh_audio_write("output.wav", interleaved_data,
               2, num_frames, 48000, 24, err, sizeof(err));   // WAV
mh_audio_write("output.flac", interleaved_data,
               2, num_frames, 48000, 24, err, sizeof(err));   // FLAC

// Get file info without decoding
MH_AudioFileInfo info;
mh_audio_get_file_info("song.wav", &info, err, sizeof(err));
printf("Duration: %.2f seconds\n", info.duration);

// Resample audio (e.g., 44.1kHz -> 48kHz)
MH_AudioData* resampled = mh_audio_resample(
    audio->data, audio->channels, audio->frames,
    44100, 48000, err, sizeof(err));
if (resampled) {
    printf("Resampled: %u frames at %u Hz\n", resampled->frames, resampled->sample_rate);
    mh_audio_data_free(resampled);
}

VST3 Preset I/O

Portable .vstpreset reader/writer with no external dependencies:

#include "minihost_vstpreset.h"

char err[256];

// Read a .vstpreset
MH_VstPreset preset;
if (mh_vstpreset_read("in.vstpreset", &preset, err, sizeof(err))) {
    // Apply the processor chunk to a plugin
    mh_set_state(plugin, preset.component_state, preset.component_size);
    mh_vstpreset_free(&preset);
}

// Auto-detect the processor FUID from the plugin bundle's moduleinfo.json
// (requires VST3 SDK 3.7.5+, which all modern plugins ship).
char class_id[MH_VSTPRESET_CLASS_ID_LEN + 1];
if (!mh_vstpreset_read_class_id_from_bundle(
        "/path/to/synth.vst3", class_id, err, sizeof(err))) {
    fprintf(stderr, "Cannot determine class_id: %s\n", err);
    // For legacy plugins without moduleinfo.json, supply class_id another way
    // (e.g., copy it from an existing .vstpreset).
}

// Write current plugin state to a .vstpreset
int state_size = mh_get_state_size(plugin);
void* state = malloc(state_size);
mh_get_state(plugin, state, state_size);

mh_vstpreset_write("out.vstpreset",
                   class_id,
                   state, state_size,
                   NULL, 0,  // optional controller state
                   err, sizeof(err));
free(state);

Thread Safety

  • mh_process, mh_process_midi, mh_process_midi_io, mh_process_auto: Call from audio thread only (no locking)
  • All other functions are thread-safe with internal locking
  • Do not call mh_close while another thread is using the plugin

API Reference

Detailed API documentation:

  • C API Reference -- minihost.h, minihost_audio.h, minihost_audiofile.h, minihost_chain.h, minihost_midi.h, minihost_vstpreset.h
  • Python API Reference -- Plugin, PluginChain, AudioDevice, MidiFile, MidiIn, audio I/O, MIDI rendering, automation, VST3 presets
  • Hosting Guide -- practical guide with extended examples

License

GPL3

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

minihost-0.2.1-cp314-cp314-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.14Windows x86-64

minihost-0.2.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

minihost-0.2.1-cp314-cp314-macosx_11_0_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.14macOS 11.0+ x86-64

minihost-0.2.1-cp314-cp314-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

minihost-0.2.1-cp313-cp313-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.13Windows x86-64

minihost-0.2.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

minihost-0.2.1-cp313-cp313-macosx_11_0_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.13macOS 11.0+ x86-64

minihost-0.2.1-cp313-cp313-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

minihost-0.2.1-cp312-cp312-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.12Windows x86-64

minihost-0.2.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

minihost-0.2.1-cp312-cp312-macosx_11_0_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.12macOS 11.0+ x86-64

minihost-0.2.1-cp312-cp312-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

minihost-0.2.1-cp311-cp311-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.11Windows x86-64

minihost-0.2.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

minihost-0.2.1-cp311-cp311-macosx_11_0_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.11macOS 11.0+ x86-64

minihost-0.2.1-cp311-cp311-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

minihost-0.2.1-cp310-cp310-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.10Windows x86-64

minihost-0.2.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

minihost-0.2.1-cp310-cp310-macosx_11_0_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.10macOS 11.0+ x86-64

minihost-0.2.1-cp310-cp310-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file minihost-0.2.1-cp314-cp314-win_amd64.whl.

File metadata

  • Download URL: minihost-0.2.1-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.14, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for minihost-0.2.1-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 d82a529e57ef57dc4ce00667c65bdf2bec72ee65c6e1995a005be54775825d65
MD5 43340c195908fedb9bf4145be6d55d74
BLAKE2b-256 412da5d02bc3ea1d0ecd9558f2140c59ad3ad86f76a9b76bf6d4a3ae6dbc9893

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 cfe1f7280c10abb37de5799150032b1c54d6b86d0d5505c170b7befbd3ef7a33
MD5 014020373a89f93761790940d930288a
BLAKE2b-256 08fabeac5f48a2f1f1e23331422f80dace86f86f8ed268285c11a40949fc7af3

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp314-cp314-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp314-cp314-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 fdeea9180c356acf750c1ae411ec61d0054975cc8093753e498ea497a8f79c04
MD5 aa6a0141398d023bd084e3de3c960376
BLAKE2b-256 ce6953cdd28f3719617907abdf3a3c4323d11dcfdaf7942ef7abe681f6803a5d

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 84f844eb02a500b192ff341678708e4adee12578ad16ebccfece228b7a564b7b
MD5 6bb6c84e3ad08ae53a61c9259776918a
BLAKE2b-256 e961c2d50f3ebf52a79ad4b28ae8d726f7874720704e5dac70ae5bbd3f481b04

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: minihost-0.2.1-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for minihost-0.2.1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 3de486493f38d7a13e5c75c91fd67445c8c4bad1ea2a5b30cca44d2b4bf80320
MD5 7464e8951f8c02ec7c954d13f4bf54fc
BLAKE2b-256 186aac5b67973eeb11f71b82ce04619fb4324e88c900f6da2eea1d9970dcfa67

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 291393f8edfb49ab5d9ba306b37157ac8f8663f82a8bd7db0073b30b72da8e58
MD5 f3c1e78da6e7ae1b85c433bf30e41da4
BLAKE2b-256 ea4e5b9aa793d458c5377f66e0c22c7202f2bf4025cf4b73703be84126f019b7

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp313-cp313-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp313-cp313-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 2513716faf83214cf2f236f12c12e34c6cfd0c13f4e3f036c25b3c0ee46a7ff5
MD5 41679f06e281843f10ead9a620c4c76d
BLAKE2b-256 fde026eba3383185c1e7447b35ebc2b6c92305900accb333e84bc25a1ea2c7ab

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c8a402cc8f293f239dfe8768f069b0f2d63fa018b2ff16c8b004aaf69823be27
MD5 770d5e4c45fc24d9a91ad3a6e8638d8a
BLAKE2b-256 5e5c6cbbe216e6b59ce079a3b3dfe2bf65167b1fc29641b7078c6c51bcebb0e3

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: minihost-0.2.1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for minihost-0.2.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 a4f16144ae5dee369bfe29a9705be6cec5abb80cb12038c75ed510a3fff91ced
MD5 63576abe7068134df7110cdd503f8807
BLAKE2b-256 d624cef7597cd2de911f0d62d8d765d68cb77bd85538775b8b07361c29506caf

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 1df0cbb150a7b7bdf04130d2b97dbdf88754fce871fc013154a2821f5f46417a
MD5 6053a17865fac6b4371ba1ac46846578
BLAKE2b-256 5c06a500d920bac462cf1fea9a5c68b9efe27b502e33967a07049ea7c6bfcc69

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp312-cp312-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp312-cp312-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 5d4b57a68cb0a0138aedcc72722c8fd0c246ab84ec3f471e662cf64c3841fae1
MD5 3b67e66c6a5913ac774b33a693865ef7
BLAKE2b-256 b147e68ddc5de832b738fcbb5db8620120d2fb7ed58013be7828c9132458b1a7

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1b82823992fc9e784cbddcddf90fd11558123f0183045abfa8224ad2d6abd868
MD5 39346efc657fc115c18b594032178cea
BLAKE2b-256 1eeab2590374c9d6a1207cde8296d03b2089f5fb1bc8614441ead6aa83897473

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: minihost-0.2.1-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for minihost-0.2.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 b7b12f10fe19251b03211d77120dffe57ad8240fbc1d1d3259f38a856e358a9d
MD5 32e0dc4df5fc5ac21d10713fd20ac0b1
BLAKE2b-256 e4514d187f975d4194516529b9cb6cba7d81a550c134f64803cdcde8be725a02

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 64748f226bfa6826d2c6f9670961063aea7df374776e32c3669b380ae0e3890c
MD5 24cb1bbae0d250814b8f6481d96c9b52
BLAKE2b-256 02b53ed90f46c182a28ac13455e1513c906d3e41fedbf5cc6767cbd2b88fa938

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp311-cp311-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp311-cp311-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 080d6251dfc76dc73a94c42aba3b56c840a39ae3fb67a8e9a72d64d11f2aec4b
MD5 62d49d3a088a6ebb906cd9532c6cb68c
BLAKE2b-256 7cab407d6f629177cdb11db6898eb62f7e6668b39a56e67cc73d78258d3e3c01

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 2cf1c2bd7990cf2545dc12c001bf3892e5d0984eec00a9b9ff6f43513dc06ec0
MD5 4c0a978494ded33b06599b379c7c3699
BLAKE2b-256 b73a975500100d53182adcaa526608efe50c5e4b1a51b05d45fb20e8a31613c2

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: minihost-0.2.1-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for minihost-0.2.1-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 fba9d88cb033c8836ff72c969fb910efdfddee47d704ba11f79932bff36fbf07
MD5 1519153645052e806a1b84e094e382ba
BLAKE2b-256 329b15ecb04fa9247cd6b79f002e1705f8fc1a9758a34669b03b12831eb35191

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 c35f8e81cd8a3162688140c3bb3dd769ee536e9d5b4462025594ccea5daf4630
MD5 2883650ce6df7dd958c1f8bd66993e5c
BLAKE2b-256 4bfd0fc6989b1e11a05c1d8d76c331cbbaf9b1d7e5679532de3f400825a7aae8

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp310-cp310-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp310-cp310-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 57ca374d7aca0e390f687bd2f0c68c945cb264df181730094491036a70f65893
MD5 75ff15785c62018f2fbbf865c3de9a70
BLAKE2b-256 303c73f423ece19d73bf5771e148a33862c58da84d4a22535ad9bfd370667c73

See more details on using hashes here.

File details

Details for the file minihost-0.2.1-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for minihost-0.2.1-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6d3fb6746e993d2e36deb7af6a69eac2d1014c863f19f6c14ae80c70329c00b5
MD5 cc1d0fc53202365b5b50d32a487cfa86
BLAKE2b-256 a3a9b65f5ac4d20152d47d7e84aa9dc3c3143c1e67df2de568a4b7a2fb042aa3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page