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), zero-copy channel-range views (buf.channel_view(start, count)), 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), optional Broadcast Wave (bext) metadata on WAV output (write_audio(..., bwf={...}))

  • 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

  • Preset morphing -- minihost.morph interpolates between two parameter snapshots (A/B blend) for sound-design sweeps (capture / lerp / apply / morph)

  • Thread-safe by design -- construction, destruction, and thread-affine control operations are marshaled onto a dedicated native plugin thread, so a plugin can be built on one thread and used or closed from another (only the real-time process* path is single-thread/lock-free)

  • 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 that resolves to a ready-to-use Plugin, loaded off the calling thread (safe to use/close from any thread thanks to the dedicated plugin thread)

  • 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)

Preset Morphing

Interpolate between two parameter snapshots (an A/B blend), useful for sound-design sweeps. Morphing operates on normalized per-parameter values (not opaque VST/AU state blobs).

import minihost

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

# Capture two states (e.g. after loading two presets)
a = minihost.capture_params(plugin)
# ... dial in a different sound ...
b = minihost.capture_params(plugin)

# Blend 30% of the way from A to B and apply to the plugin
minihost.morph_params(plugin, a, b, 0.3)

# Or compute a blend without applying; t can be a per-parameter sequence
blended = minihost.lerp_params(a, b, 0.5)
minihost.apply_params(plugin, blended)

Async Plugin Loading

import minihost

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

# Do other work while plugin loads...

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

# The plugin is built on a loader thread but is safe to use and close from
# any thread: minihost marshals thread-affine operations onto a dedicated
# native plugin thread. Loads are serialized on that thread, so this is
# non-blocking (not parallel) loading.
plugin.close()

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)

# Broadcast Wave (bext) metadata for film/broadcast workflows (WAV only)
minihost.write_audio("take.wav", data, sample_rate, bit_depth=24, bwf={
    "description": "Scene 12 take 3",
    "originator": "minihost",
    "originator_reference": "REF-0012",
    "origination_date": "2026-07-07",   # yyyy-mm-dd
    "origination_time": "12:34:56",     # hh:mm:ss
    "time_reference": 48000 * 3600,     # samples since midnight (timecode anchor)
})

# 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

minihost runs a dedicated native plugin thread and marshals every thread-affine plugin operation onto it -- construction, destruction, and control-plane queries (state, parameter text, program names, reset, set_sample_rate, processing precision). This makes plugins safe to build on one thread and use or close from another (which is what makes open_async work), and hardens the whole library against cross-thread use.

  • process* functions (process, process_midi, process_auto, process_double, process_sidechain): the real-time path -- lock-free, call from a single thread (typically the audio thread). Not marshaled.

  • All other (control) functions: safe to call from any thread.

  • Reconfiguring calls (set_sample_rate, set_state, set_processing_precision, set_non_realtime, reset) must not overlap a process* call -- they reconfigure the audio pipeline while process runs unprotected. Stop processing before calling them.

  • Set the environment variable MINIHOST_MESSAGE_THREAD=0 to disable the plugin thread (operations then run inline on the caller's thread; cross- thread plugin use, including open_async, becomes unsafe).

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.4.1-cp314-cp314-win_amd64.whl (1.3 MB view details)

Uploaded CPython 3.14Windows x86-64

minihost-0.4.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.4.1-cp314-cp314-macosx_11_0_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.14macOS 11.0+ x86-64

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

Uploaded CPython 3.14macOS 11.0+ ARM64

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

Uploaded CPython 3.13Windows x86-64

minihost-0.4.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.4.1-cp313-cp313-macosx_11_0_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.13macOS 11.0+ x86-64

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

Uploaded CPython 3.13macOS 11.0+ ARM64

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

Uploaded CPython 3.12Windows x86-64

minihost-0.4.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.4.1-cp312-cp312-macosx_11_0_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.12macOS 11.0+ x86-64

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

Uploaded CPython 3.12macOS 11.0+ ARM64

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

Uploaded CPython 3.11Windows x86-64

minihost-0.4.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.4.1-cp311-cp311-macosx_11_0_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.11macOS 11.0+ x86-64

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

Uploaded CPython 3.11macOS 11.0+ ARM64

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

Uploaded CPython 3.10Windows x86-64

minihost-0.4.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.4.1-cp310-cp310-macosx_11_0_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.10macOS 11.0+ x86-64

minihost-0.4.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.4.1-cp314-cp314-win_amd64.whl.

File metadata

  • Download URL: minihost-0.4.1-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 1.3 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.4.1-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 84c7d2e5ea257ea5bd7e9ecf3ffc62eb564d800cfa7d0b3106e92b626cd76e10
MD5 045a7d2c666fff456285f410c50ada13
BLAKE2b-256 a56bb86a1a75c0ed2e28f1fb8f838f045017e6ff2f8dab8fc806cf4ba427022d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8d071be0f83fc62ec52aa70c011f148e56cacc6e720926e133cdbcfccd9d0413
MD5 864c84d974c3f927a6f3d3f3318c21dd
BLAKE2b-256 5c39cce1a494a3f3c3413edd5fe90041a3dbcfb0f88390d03c940346daa6b258

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp314-cp314-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 aebb9fc4db715c560b6ec71add8d9ac1aba238a484c3f1c6f7b30103f602744b
MD5 dc253ecff65f11276116327c0ba2552e
BLAKE2b-256 57a91fa0b26150524353b5cb86dffb98281884d630bd1c430ace6d16091df984

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 fd2fbbb54fd5bd7c7ae77aa0ab5e2746a93dc90239170cbb0b0134c1a81517c5
MD5 cf5003d021dfdbfdbde58ab9647c3690
BLAKE2b-256 dea6747933b920a840424dcb12324bbd3f01a7617accb66469121564bc3ae143

See more details on using hashes here.

File details

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

File metadata

  • Download URL: minihost-0.4.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.4.1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 a709f5e22c8b03a4d9d079bbfdf229f7e25f21fd78e38553302913f6ab3cef91
MD5 de66e9a37d355eafc7733fa065b9b66c
BLAKE2b-256 c01e10919588ef7e2eacefc3d84e42781ac2d145e8bf8fc7b20a8b2e58ce2a40

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 a7762dfe2e15b30cac4e3bc58ab6e37e2ce396dc452549e9c1d74c720e5217f8
MD5 a78fc5ab38c9658204894f2d567529b7
BLAKE2b-256 0cab3b8e057c5be256c7dd966cde5c50aea8fabf2a90f521a1c281eaa8454944

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp313-cp313-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 48128a764a9552f25a67f417cb1ee18e28334b1fb058bcff6b3232eadf83638b
MD5 ac281f0a1a4604ba0b1e1149f027144e
BLAKE2b-256 9b0c42dd5d0011e9bf834e2ec00b764a1d641fd78fd244e6a61ca5212c9ec1a2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 55d4a38b5afbf848397755c2516800679360d826efb72401ac37138911e397d2
MD5 5e1c103f81414e30525caf1e8f478589
BLAKE2b-256 d0b7d55415044b8e32b0f072f2b30e49b1fffb5cd49485d3871000189a2dec3a

See more details on using hashes here.

File details

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

File metadata

  • Download URL: minihost-0.4.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.4.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 337ee281d408f71a02243d48cf5b0f157f05346f5141312d9c2920d672ab9000
MD5 f4f1518e4aaa083ba76049e12e3b82a3
BLAKE2b-256 ae8336f148b6fc58ad759fe7ecdfe77d3cc256ff027e1f640e15f09864ef1854

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 45a68de69542ca1f16bc318c51ce2ca28d47ec0f46aa6b45abdab081443de3c2
MD5 d96ece69bfa6a8d8078ff809acffeea3
BLAKE2b-256 8b6973e1a408a3dc7d23f4c0fdc901b02492f8bdc34a8ffcc779390c3bd8c6d2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp312-cp312-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 56970c83cf26cab7fc3301b44e187b15a953ea3e32f074d82023a575381a87b3
MD5 4711731742a5f1b4822ceae9a9562e86
BLAKE2b-256 87746d0c2454abb8fccfcda4c804fa2cf56158d3889875b02a8211f53cb0a00a

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9feac7d32c99e1f3265b47412dffed86b5b99ad0a601a5d5d6897c53b429fdb8
MD5 dd71e4b3ccb6dc655342f8888764a8e5
BLAKE2b-256 2c14c0500572c8794b176ecfd831dd9f5f56c8c33e59bdf7bb36a4d66155ee6f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: minihost-0.4.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.4.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 cc8be74f2103d3378a9eddd2945767955f23ee9103045ba677c9836119864a6e
MD5 c8cfcbff4f0d2a1c14e6b30c06d5de6d
BLAKE2b-256 dc84a19a1ea35db61e13119740b088fba74b57927b409460f5d3b2d03a94054c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7a3a352c305e6475c5f00356305c938200bd20f67af192889002ab07bc16489d
MD5 3e9534d2d48e5dd4b113eae9a7aece18
BLAKE2b-256 17edd9d9822bf373b910020a1fdb3bf383a08f9cd3f7b2fb799b3297469263cf

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp311-cp311-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 322b764c549260a2fc5618093bb4ee0115b3340afb02d784a6e65689f341a49c
MD5 d6609b3bbec1aedff7efaa451783341c
BLAKE2b-256 f559b07d1120dffb3694e0478b48839d3e0634e8e61380911dde5d44c17a1c81

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4b53b5456ab75a805fba0a2ba20f9acd2f37b98b6a3b6b1d4c00cfdcab39f776
MD5 120ca53002275379b6df2d4fcc55d213
BLAKE2b-256 8ff94e8163f134f4ede8a92b29ce4a87065b31fd08b7ec14347d103f19a8efee

See more details on using hashes here.

File details

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

File metadata

  • Download URL: minihost-0.4.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.4.1-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 a536503e03469448b5106d526fa969aa240f6ed5fefd3d5b29af12cb08c51b3d
MD5 69469bb955b71ea89dfa9b32dfceace7
BLAKE2b-256 fb044d5c994ae7c00caad417336c4dc0454afb93499daabdd91133e931098ea4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 00c2a3f3f6e02323fd2122ec1cbd7e786f245356771f3fb86a00ceffcbbf35cf
MD5 dfe05c0fe97ae8d3b5356a234b667cde
BLAKE2b-256 1a20d0512c6f72119a89bbdf7319811b9e219aa37711b71b90ed75b3ff4c0e45

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp310-cp310-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 b7614f845bd2aa7ecfbdbc1f0bb6466723d77691845c889b2641fedd8e9cab5f
MD5 b8088fc7d2e57cfce573830ad5fbf962
BLAKE2b-256 a4460e0bef5d681275896992d4d8b2cb88a6a986b5b0ad33c110ade53f2909cc

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for minihost-0.4.1-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 79564fb3073273276bfb9dada095ef2b11d2f369468f0f6ad8f35046586297cb
MD5 eca176c963475f9bc3fae66e78fe307e
BLAKE2b-256 071ebf712261615e698e9df3858e01db3545b793853090f7acb5bb28bf1a21bf

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