Skip to main content

audiodsp

CircuitPython's audio system for MicroPython and CPython — audiocore, audiomixer, synthio (including MidiTrack), the effects modules (audiofilters, audiodelays, audiofreeverb, audiospeed), audiomp3, and CircuitPython play()/stop()/pause()/resume() output semantics. It adds modules CircuitPython doesn't have, among them audiovorbis for Ogg Vorbis files. Import names stay audiocore/synthio/etc., matching CircuitPython for source compatibility.

audiodsp was named audioif until 2026-09-21; that name now belongs to the audio hardware layer. The modules you import did not change. The helper package is audiodsp_util now, and the distribution on PyPI is pydevices-audiodsp.

New here? See the newcomer's guide for the runtime map, shared-core architecture, and contributor boundaries.

Installation

MicroPython 1.29 or later takes this repository with one line in the manifest your build already uses. Clone it anywhere, fetch its two pinned native dependencies, and include its manifest.py:

git clone https://github.com/PyDevices/audiodsp ~/build/audiodsp
~/build/audiodsp/scripts/fetch_deps.sh
include("/home/you/build/audiodsp/manifest.py")

On unix the manifest to edit is ports/unix/variants/standard/manifest.py; on esp32 and rp2 it is usually ports/<port>/boards/manifest.py, unless your board brings its own. Then build the way you always do. The include brings in the audiodsp C modules (named with c_module()); it freezes no Python and brings no ulab, which audiodsp doesn't use: a build that wants ulab names it itself. fetch_deps.sh puts the mp3 decoder under .deps/, pinned by DEPENDENCIES.lock; the mp3 glue looks there first and falls back to a sibling checkout beside this repo. Tested on the unix port against MicroPython v1.29.0, where synthio, audiomp3 and audiopump all import.

If you would rather not edit the MicroPython tree, write a manifest of your own and pass it as FROZEN_MANIFEST=. That replaces the port's default, so include the default too (include("$(PORT_DIR)/variants/standard/manifest.py") on unix, include("$(PORT_DIR)/boards/manifest.py") on esp32) or you lose asyncio and the port's other frozen modules.

To build without mp3, skip the fetch and set AUDIODSP_OPTIONAL_DEPS=1 on the build step — micropython.mk/micropython.cmake read it, not fetch_deps.sh, and on CMake ports it is an environment variable, not a -D cache entry.

Older than 1.29? Manifests there have no c_module(), so do not include this one; use USER_C_MODULES. On esp32 and rp2 point it at this checkout; on a Make port point it at the directory that contains the checkout, which builds every module in that directory.

MicroPython's manifest reference explains include() and c_module(), and micropython-pydevices keeps ready-made manifests and boards for several PyDevices modules at once. docs/porting-plan.md covers the architecture, module tiers and testing strategy.

CPython 3.11+ installs from PyPI:

python -m pip install pydevices-audiodsp

This gets you audiocore, synthio, audiomixer, audiofilters, audiodelays, audiofreeverb, audiospeed, audiodynamics, audioroute, audiomath, audioecho, audioshaper, audioladder, audioconvolve, audiobiquad, audioverb, audiomodal, audiometer, audiopump, audiovorbis, and the audiorender package. audiomp3 remains firmware-only. The distribution declares no required runtime dependencies and does not itself publish an audiodsp import; its version is the VERSION file, which is also what _audiodsp.__version__ reports.

audiorender is the exception, and it is opt-in: it is numpy throughout, so numpy comes with the render extra rather than with the wheel, keeping the core dependency-free for boards and wasm:

python -m pip install "pydevices-audiodsp[render]"

The instrument and effect libraries — audioinstruments (55 synthesizers, keyboards and drum machines) and audioeffects (45 effect classes, racks included) — are not part of this distribution. They live in the audiocomponents repository as their own packages, each depending on pydevices-audiodsp; see that repository for how to install them.

What's here

Two pure-Python modules sit beside the CircuitPython-compatible core:

  • lib/audiodsp_util/ — float32(value) and float32_bits(value), struct and nothing else. A Python float is the interpreter's own width: a double here, a single on every board. So a setting written in Python — node.mix = 0.35 — is two different numbers, and the node renders different bytes on a board than on a desktop before its kernel is reached. Put a derived setting through float32 and both targets hold the number the board would have held. See docs/correctness-standard.md.
  • lib/audiorender/ — renders a whole composition offline: tracks, tempo map, notes and automation in, a mixed stereo master and a level report out. This is the one part of the repository written for a desktop rather than a board — numpy throughout, a whole song in memory — so it ships in the wheel and is never frozen into firmware. It has its own README.

The instrument and effect libraries that used to sit beside it — audioinstruments and audioeffects — now live in the audiocomponents repository, together with the audio component contract they implement: the metadata manifest (docs/audio-components.md) and the runtime API (docs/audio-component-api.md). audiorender drives any component that speaks that API and does not need those packages installed.

Additions beyond CircuitPython

These are not CircuitPython's. audiodynamics (compression, limiting, expansion, gating, transient shaping) and audioroute.Splitter (fan one stream out to parallel branches) come from micropython-vst3's audio engine, which had them and CircuitPython does not. audiomath (multiply one stream by another — ring and amplitude modulation; and divide one down in frequency — the analog octave divider), audioecho (a delay with a filter, a soft-clip and a cross-feed inside its feedback loop), audioshaper (a waveshaper whose curve is data, applied above the sample rate, and a sample-and-hold at an exact rational ratio), audioladder (a transistor ladder filter — four one-pole stages round a feedback loop with an odd saturator inside it), audioconvolve (apply a measured or synthesized impulse response, by partitioned FFT), audioverb (a reverberation tank whose line lengths and output taps come from Python), audiomodal (a bank of resonators, which is what a struck object is), audioroute.MidSide (scale the difference between a stereo pair's channels) and audiobiquad (below) have no ancestor anywhere and are audiodsp's own. Every one of them builds into CircuitPython 11 too, as a user C module from this repository's micropython.mk (make BOARD=... USER_C_MODULES=/path/to/audiodsp in a CircuitPython port), and plays through CircuitPython's own audiobusio, audioio and audiopwmio; there, audiocore, synthio, audiomixer and the effects modules stay CircuitPython's. audiometer (band levels for a spectrum meter, below) is ours as well, and is the one CircuitPython doesn't get: its sources are a board's pump and usbif's sound card, and CircuitPython has neither. audiovorbis (below) plays Ogg Vorbis files, which CircuitPython has no module for.

audiovorbis.VorbisDecoder — Ogg Vorbis files

Shaped like audiomp3.MP3Decoder, so it plays wherever an MP3 does: through audiocore.get_buffer(), a Mixer voice, or a board's audio output.

import audiovorbis
song = audiovorbis.VorbisDecoder("song.ogg")   # or an open binary file
mixer.voice[0].play(song, loop=True)

It takes the same arguments (a file or a path, and an optional buffer) and has the same file, open(), rms_level, samples_decoded, deinit() and with as MP3Decoder. Mono and stereo files play; sample_rate and channel_count come from the file. Like an MP3, it reads its file while it plays, so audiopump refuses it: feed the pump from the interpreter instead.

The decoder is Xiph's Tremor, integer-only, carried in third_party/tremor under its own BSD-3-Clause licence. It decodes to the same samples on every build, and within two steps of ffmpeg's float decoder. Other C modules in the same firmware can decode a stream through it too. docs/vorbis.md has the C interface, what it needs of memory on a board, and how it's tested.

audiobiquad — filters whose tails reach exact zero

audiofilters.Filter (over synthio.Biquad) and audiofilters.Phaser are ported CircuitPython and their recursions are integer. Both have states that reproduce themselves: fed silence after a burst, they hold a constant for ever. audiobiquad is the same two filters with float state and a flush of anything below 1e-20, so a tail decays to zero and stays there — and its all-pass feedback is not clamped to 0.1..0.9, so zero is zero and a feedback-free phaser's notches are true nulls. It is also the one to reach for below about 100 Hz, where the integer coefficients run out of resolution — which filter, and why.

import audiobiquad, synthio

eq = audiobiquad.Biquad(mode=audiobiquad.PEAKING_EQ, frequency=3200,
                        Q=1.2, gain_db=-4.0, sample_rate=48000)
eq.play(source)

sweep = synthio.LFO(rate=0.4, scale=400.0, offset=900.0)
phase = audiobiquad.AllPass(stages=4, frequency=sweep, feedback=0.0,
                            mix=0.5, sample_rate=48000)
phase.play(eq)
Biquad AllPass
constructor mode, frequency, Q, gain_db, mix, sample_rate, channel_count stages, frequency, feedback, mix, sample_rate, channel_count
block inputs frequency, Q, gain_db, mix frequency, feedback, mix
fixed at construction sample_rate, channel_count those, plus stages (it sizes the state)
methods play, stop, clear play, stop, clear
read-only playing, coefficients playing, stages, coefficient
latency 0 samples 0 samples

When a Biquad's frequency, Q, gain_db or mode changes while it plays, it glides to the new filter over about 20 ms instead of jumping, so a preset change that drops the corner from 20 kHz to 20 Hz doesn't thump. A block input glides the same way, so an LFO on frequency arrives about a block late. mix still moves at once, and so does a filter that hasn't played yet.

mode is one of LOW_PASS, HIGH_PASS, BAND_PASS, NOTCH, PEAKING_EQ, LOW_SHELF, HIGH_SHELF — numbered exactly as synthio.FilterMode numbers them, so either can be handed to either. mix crossfades: 0 is a wire, 1 is the filtered signal alone; for an all-pass cascade 0.5 is the equal sum a script phaser makes, and where its notches are deepest. AllPass's frequency really is the frequency at which one stage's phase passes −90°, so nothing has to pre-warp it. Both nodes hand out 256 frames at a time, sit in an audiosample chain like any other effect, and never report themselves finished — a starved chain gets silence. Q is bounded to 0.05..60 and feedback to ±0.99: a pole on the unit circle is a filter that never stops ringing, which is the one thing this module exists to avoid. See docs/upstream-diff.md for the measurements.

audiodynamics.Dynamics carries twenty-one options beyond what the engine gave it, added for the effects program and every one of them default-off: an RMS detector, a feedback detector topology and an external key input with Key Listen, a 4x true-peak reconstruction, a two-pole key band with both corners settable, a settable expander/gate depth, a four-stage gate envelope with hold and hysteresis, a relative-threshold gain computer, a program-dependent attack, and the transient shaper's four detector time constants, a second envelope pair and a peak-hold on the slow one. What each is for, what it was measured doing, and what it cost: docs/upstream-diff.md.

audioecho.FeedbackDelay takes four further options, each off at its default. wow_shape swaps the built-in modulation sine for one period of your own — int16 Q15, a power-of-two count from 2 to 4096 — because a bucket brigade's delay is its line length over its clock, so a triangle on the clock is a reciprocal on the delay and no sine is that. delay_slew walks the read head to a new delay_ms at a constant rate (delay-seconds per second) instead of jumping to it, which is both an Echoplex's varispeed and the reason a delay-time knob can be turned mid-take without a click. wow_am_depth puts the same oscillator on the wet gain, dipping only. loop_semitones pitch-shifts the line read inside the loop, so every repeat rises again — a shimmer, which chaining audiodelays.PitchShift after a delay is not. Shifting up, the climb fades out at the top of the band rather than folding back past Nyquist. See docs/upstream-diff.md for what each was asked for and what it measures.

If you want the delay in frames rather than milliseconds, give delay_frames instead of delay_ms: the line is read exactly that many frames back. At 44.1 and 22.05 kHz some whole frames have no delay_ms that lands on them, so a lossless repeat at those rates needs delay_frames.

audioroute.MidSide(source, width=1.0, sample_rate=48000, channel_count=2) takes a stereo pair apart into its mono sum and the difference between its channels, scales the difference, and puts the pair back together. width=0 collapses to mono, 1 passes through, 2 doubles the sides; values outside 0..2 clamp to those rails. play(sample) sets the source, set(width=...) moves the width mid-stream. There is no state and no latency — and at width=1 the output bytes are the input bytes, exactly, for every int16 pair, so the node costs nothing to leave in a chain that is not using it. A mono source passes through: there is no difference to scale. It exists for the drive classes as much as for stereo width: a nonlinearity applied to a stereo pair intermodulates its channels, so a saturator that wants to keep its image drives the mid and leaves the side alone.

audioshaper.Waveshaper(sample_rate=…, curve=…, oversample=4, channel_count=2, **options) moves everything but its first four arguments in set(). curve is int16 Q15, at least two points, spanning −1..+1 of input: compute it once on a desktop and ship it as data, never rebuild it on a board, whose float is single-precision where the desktop's is double. oversample is 1, 2, 4 or 8 — the shaping happens there, between a matched pair of polyphase all-pass half-bands, because a nonlinearity at the base rate folds the harmonics it makes above Nyquist straight back onto the signal. pre_gain is the drive knob (gain into one normalised curve, never a curve rebuilt per knob move), bias moves the operating point, post_gain follows the curve, and mix is a straight 0..1 crossfade. hysteresis is off by default; above zero it gives the curve a memory, so a slow signal in and out traces two paths and encloses an area, with hysteresis_width its half-width as a fraction of full scale at the input and hysteresis_bias splitting that between the rising and falling branches. audioshaper.GROUP_DELAY_SAMPLES carries the measured group delay per factor — 0, 2.2, 3.3 and 3.9 base samples — for a component that has to report its latency. A curve that reaches the rails has a ceiling of its own: the decimator can ring past them on a hard edge, so keep post_gain * max(abs(curve)) at or below audioshaper.CLIP_HEADROOM (0.74) and put the rest of the wanted level on a mixer voice after this node. Why each of those is the shape it is, and what the oversampling measures: docs/upstream-diff.md, "audioshaper: audiodsp's own, and the two things a fixed curve cannot be".

audioshaper.SampleHold(source, num=…, den=…) is the same module's other node: a zero-order hold in which num source frames carry den new values, so 26 040 Hz at 48 kHz is 400/217 and is exact — one frame in, one frame out, at the source's own rate, channels and bit depth, with an integer accumulator that cannot drift however long the render runs. num == den is a wire byte for byte, latency is 0 (what a hold displaces is an event landing on a frame it drops, up to ceil(num/den) − 1 frames, which is the effect and the caller's to report), and set(num, den) moves the ratio mid-stream. It exists because the palette's sample-and-hold was a pair of audiospeed.SpeedChanger nodes whose 16.16 rates cannot be made reciprocal: measured on that hold, the pair ran 0.9999947 of the rate it asked for and flanged a static setting by 10.74 dB over twelve seconds. The rate form was not added to SpeedChanger because that module is CircuitPython's and an argument added here would not exist on a board — docs/upstream-diff.md, "audioshaper.SampleHold: lo-fi's other half".

audioladder is the one whose reason for existing is least obvious next to a module CircuitPython already has. audiofilters.Filter is a better resonant low-pass than this: a cascade of biquads tracks the analytic response to a fraction of a decibel. It is also linear, so it never sustains a tone of its own, has no harmonics to speak of, and sounds exactly the same however hard you hit it — and those three are what a ladder is. The whole surface:

import audioladder

ladder = audioladder.Ladder(
    sample_rate=48000,      # and channel_count=1 or 2
    cutoff_hz=800.0,        # where the filter turns over
    resonance=3.6,          # 0..4.2; at 4 it sustains a tone at the cutoff
    drive=2.5,              # linear gain into the loop, 1 being unity
    poles=4,                # tap stage 1..4: 6, 12, 18 or 24 dB an octave
    passband_comp=0.0,      # 0..1, how much of the passband to give back
    oversample=2,           # 1 or 2; 2 folds back fewer of its harmonics
    mix=1.0,                # 0 a wire, 1 the filter
)
ladder.play(source)
ladder.set(cutoff_hz=1200.0)   # any option, mid-stream, once a block
ladder.clear()                 # empty the integrators; stops a sustained tone

See docs/upstream-diff.md for why its feedback loop is solved rather than delayed, and what that was measured to be worth.

audiomodal.Bank

A bank of resonators, which is what a struck object is. Hit a drum head, a marimba bar, a bell or a wine glass and it rings as a sum of decaying sinusoids at frequencies that are not harmonics of anything; hand Bank that list and a short noise burst for the stick, and it is that object.

kick = audiomodal.Bank(modes=9, sample_rate=48000, channel_count=1)
kick.set_modes(((58.0, 0.55, 1.00), (92.4, 0.24, 0.38),
                (123.9, 0.15, 0.24), (2200.0, 0.010, 0.30)))
kick.play(stick)          # a short noise burst
audio_out.play(kick)

Construction fixes the allocation: sample_rate, channel_count (1 or 2) and modes (1 to 64). set_mode(index, frequency, decay, gain) places one mode and set_modes(table) places the whole list; decay is a 60 dB time in seconds, not a Q, and gain is the peak of that mode's impulse response, so a modal table read out of a paper goes in as published. set() moves mix and gain mid-stream without stopping anything ringing, clear() stops everything at once, and ringing says whether any mode still holds energy.

Two behaviours worth knowing before you use it. A bare Bank() is silence, not a passthrough — the default mix is fully wet and an unconfigured bank has no modes — so reach for mix=0.0 if you want the untouched signal. And the tail keeps ringing after the source stops, as audioecho.FeedbackDelay's, audioverb.Tank's and audioconvolve.Convolver's do, because a drum whose decay ended the instant the stick left would be the one thing this node exists not to be.

See docs/upstream-diff.md for why this is a node rather than a chain of audiobiquad.Biquad, measured rather than argued.

audioverb.Tank

Dattorro's plate reverberator, with the network handed in rather than compiled in — which is the difference from audiofreeverb.Freeverb, whose comb and all-pass lengths are constants in a CircuitPython-ported kernel.

plate = audioverb.Tank(
    sample_rate=48000, channel_count=2, max_predelay_ms=200,
    decay=0.7, diffusion=0.75, bandwidth_hz=9000, damping_hz=4500,
    mod_rate_hz=1.0, mod_depth_ms=0.27, mix=0.35)
plate.play(source)
audio_out.play(plate)

Construction fixes the allocation and the topology: sample_rate, channel_count (1 or 2), max_predelay_ms, delays and taps. delays is twelve line lengths in frames — the four input diffusers, then each tank half's modulated all-pass, delay, all-pass and delay. taps is four values per tap (channel, line index, offset in frames, gain), at most 32. Both default to Dattorro's published table, scaled from 29761 Hz to sample_rate.

set() moves the rest, mid-stream and without emptying the lines: decay, diffusion, damping_hz, bandwidth_hz, low_cut_hz, predelay_ms, mod_depth_ms, mod_rate_hz, drive, width, tone_db, mix. Every filter is out of the path at zero, so a bare Tank() is that network with nothing added to it; mix follows audiodelays.Echo's 0..2 convention, dry at unity until 1. clear() empties every line and every filter. Latency is zero dry-to-wet, and the tail reaches exact zero rather than sitting at one LSB — every line write is a magnitude truncation, which is the standard cure for a recirculating fixed-point network's limit cycles. When the source ends, the tail rings out on silence to that zero and the node rests, so a source played into it afterwards meets an empty plate; audioecho.FeedbackDelay and audioconvolve.Convolver do the same. See docs/upstream-diff.md for the measurement, the cost and the memory the default table needs.

audioroute.Port — the wire a consumer holds

A zero-copy pass-through you can re-point while it is playing. Its get_buffer hands back its source's own pointer, length and result; its play(source) swaps that source atomically against a pull in flight. Hold a port, wire it into a mixer voice or a rack, and change what is behind it as often as you like — the object the consumer took never changes.

import audioroute

port = audioroute.Port(overdrive)
mixer.voice[0].play(port)       # take it once

port.play(bitcrusher)           # heard on the next block; nothing rewires
port.source                     # what it is playing right now

That is the problem it was built for. About twenty effect classes rebuild part of their graph when a knob crosses a threshold and hand out a new node afterwards — and a consumer holding the old one goes on playing the graph the class has finished with, or goes silent. A port turns that into a re-point. play() does the protocol lookup, the deinit check and the format match first, on the calling thread where raising is free, then takes the pump lock for three stores, so a pull in flight sees the whole old source or the whole new one and nothing in between.

It costs about 20 ns a block on x86-64 and 1.27 µs a block on an ESP32-P4, both measured as the slope of 0, 25, 50 and 100 stacked ports over the same 2000-block probe; against a 256-frame block at 48 kHz that is four ten-thousandths of one per cent on the desktop and about two hundredths of one per cent on the board.

The board figure replaces an estimate of 700 ns that was extrapolated from four effect classes timed on both machines, and the estimate was low by 1.8x. The real ratio here is 64x, not the 32–35x those classes gave: a port's work is a protocol lookup and three stores with almost no arithmetic, so it does not scale the way a DSP node does. That is an argument against extrapolating a per-class cost at all (#113).

Port has no stop(), deliberately — a port always has a source. A port pulled while it is already inside itself, which is what a component wrapping its own output builds, publishes a loop fault and hands back an error in 17 us instead of recursing until the stack is gone.

audiometer.Meter — band levels for a spectrum meter

N log-spaced bands between two frequencies, plus peak and RMS, one byte each in half-dB steps (0 is -100 dB or quieter, 200 a full-scale sine).

meter = audiometer.Meter(32, low_hz=35, high_hz=20000)
meter.feed(pcm, 48000, 2)            # interleaved s16, from Python
seq, levels, peak, rms = meter.levels()

On a board it can listen without Python touching a sample. meter.attach(tap, 48000) reads an audiopump.Tap in C each time you call levels(), so any pump stream can be metered. meter.attach(audiometer.UAC) is fed by usbif's sound card in its own pump, on the other core: on the P4 panel, 1.6 % of that core for the feed and 2.7 % for the analysis with 44 bands, and the card's delivery doesn't move. seq counts analyses, 60 a second of audio, so a reader can tell fresh levels from a stopped stream.

Two FFTs, because one can't be both quick and sharp at the bottom of a log scale: 1024 points for the bands above 400 Hz, and 512 points on a copy low-passed and decimated by 16 for those below. A band's level is the spectrum integrated over the band, so pink noise reads flat to within about 1.5 dB across 35 Hz to 20 kHz. The default floor is 35 Hz because below that real music barely moves a bar. The window, the filter, the band edges and the dB rounding avoid libm, so a desktop and a board read the same bytes for the same samples; tests/parity/meter_probe.py checks that.

audiopump — the audio pull, off the interpreter thread

audiopump runs the same pull a player runs, in C, on a thread the interpreter is not on: a task pinned to the other core on esp32, a native thread on unix and Windows, and the calling thread on a port that has no threads at all. A graph pulled that way keeps an exact clock while a screen redraws, a USB stack runs and Python does whatever it likes.

The thread and the output it writes to come from a platform driver, which this repository doesn't carry; the end of this section says where it comes from. The example below is for a board whose driver has opened its output. On a build without one, such as unix built from this repository alone, sink=True raises ValueError: no i2s sink open, and the service() form under Which builds carry it is the one to use.

import audiopump, audiomixer, synthio

synth = synthio.Synthesizer(sample_rate=48000, channel_count=2)
mixer = audiomixer.Mixer(voice_count=1, sample_rate=48000,
                         channel_count=2, buffer_size=2048)
mixer.voice[0].play(synth)

status = bytearray(audiopump.STATUS_BYTES)
audiopump.spawn(mixer, 0x7FFFFFFF, status, sink=True)

synth.press(60)                # while it plays; no parking, no ceremony
mixer.voice[0].level = 0.6
audiopump.shutdown()           # and a soft reset does this for you

spawn(sample, blocks, status, …) starts it; pull() runs the same loop on the calling thread, so micropython.heap_lock() around it is a real gate on "the pull does not allocate"; service() advances it where there is no thread; retarget(sample, loop=…) points it at a different tail; shutdown() stops it. status is a bytearray of counters — blocks, bytes, a digest of everything pulled, the worst block, what the sink clocked, the error and the fault — read back with struct.unpack. Everything that can refuse happens on the calling thread. The pull itself never raises, because there is no interpreter on that thread to raise on, so it publishes a fault code and stops.

retarget's loop= travels with the swap rather than being stored when you call it: the old tail is pulled until the next block boundary, so setting the flag early ends the pump one block before the new tail ever runs. Leave it out and the flag stays as spawn() set it, which is what every caller before the argument existed wanted. Get it wrong the other way and a looping client left alone on a live pump stops at the end of its lap.

fade= — changing what plays without a click

Swapping one graph for another mid-waveform is a seam you can hear, and a change that holds the pump for longer than its ring lets the speaker run dry, which cuts the sound off and clicks again when it comes back. Both calls that change what plays can fade instead:

rack = build_the_next_patch()              # off the lock, as long as it takes
audiopump.retarget(rack.output, fade=True)  # old fades out, new fades in
old_rack.deinit()                           # safe: True means it is let go

audiopump.park(fade=True)   # hold the graph while you rewire it
rewire()                    # however long this takes, the speaker is fed
audiopump.unpark()

retarget(tail, fade=True) sends the current tail's next block out faded to silence, swaps, and fades the new tail's first block in: one block each way, 5.3 ms at 256 frames and 48 kHz. A freshly built graph's first block is often its slowest, and with the fade that lands in the gap rather than in a cut. It returns True once the pump has stopped pulling the old tail, so you can release it; False if it stopped waiting (the pump is parked, asleep on a full ring, or timeout_us, 100 ms by default, ran out). The swap still happens on the next block, and the old tail is kept until then.

park(fade=True) is a park with a deadline. The pump pulls one block ahead and keeps it. If you unpark() before the speaker's queue is down to that block, it goes out untouched and nothing is audible. If the queue gets there first, the block goes out faded, the pump feeds the speaker silence for as long as the park lasts, and the first block after unpark() fades in. It needs a pump on a thread of its own and a ring, or a sink whose driver counts what it clocked; where nothing can say how full the queue is, it fades at once.

fades() says what happened since spawn(): (fades, clean holds, silence bytes). The status block's digest is still the graph's own audio; only what goes to the speaker, the ring and the tap is faded. Without fade=, both calls behave exactly as before.

backpressure() — whether a full ring makes the pump wait

>>> audiopump.backpressure()
True

True where a full output ring makes the pump wait for room instead of dropping the block; False where a free-running pump would lose audio. It is the question a driver asks before it decides whether to park the pump between ticks, and it is True in two different ways: on a threaded port whose driver fills in park_spin, where a wake ends the wait, and in service mode, where the loop hands the thread back on a full ring and the caller's next service() is the wake. It is False only on a threaded build with no park_spin — a driver that is not finished, which now says so rather than quietly losing blocks.

It is worth asking because parking costs the caller. On the desktop unix build, ten seconds of a synth through an Overdrive and a TapeDelay with an app doing 3.5 ms of work a tick: the interpreter spends 52 ms per 10 s on audio against 862 ms on the old interpreter-thread path, where a parked pump cost 923 ms. With no app work at all the two are level (758 ms against 713). A parked pump used to spin 94 % of one core and now sleeps at 1 %. A WAV plays back byte-identically ten times out of ten with all eight cores of that box in a busy loop, overflow count 0 by construction; with the drop put back, 0 of 10.

Three kinds of source feed it and they are all the same kind of thing to it: a graph; audiopump.Ring, an audiosample node Python writes PCM into, so a pushed stream can sit behind a Mixer with effects on it like anything else; and audiopump.Events, a queue of frame-stamped presses, plays and levels the pump applies at block boundaries, which is what puts a sequenced bar on the audio's clock instead of the interpreter's. audiopump.Tap reads what is going out, for a meter or a scope, without being in the path.

Which builds carry it. Every MicroPython port: unix, Windows, WebAssembly and esp32. The CPython wheel does too, in the shape a port with no thread has: driver() is 'none', threaded() is False, spawn() returns −2 and adopts the graph, and nothing is pulled until you call service(), which fills the ring you passed to spawn() and returns when it is full. The names, the status block, Ring, Events and Tap are the same, and so are the bytes: drained from the ring, a graph is byte-identical to the same graph on the MicroPython unix build.

status = bytearray(audiopump.STATUS_BYTES)
ring = bytearray(16384)
audiopump.spawn(mixer, 0x7FFFFFFF, status, ring=ring)
buf = bytearray(4096)
while playing:                 # from your app's timer, say every 20 ms
    audiopump.service()
    n = audiopump.drain(buf)
    sink.write(memoryview(buf)[:n])

audiodev does this for you: on CPython its players drive the pump from their own tick. What CPython does not have is pull() under a heap lock, because it has no heap lock, and audiocore's pull-depth cap: a graph that leads back into itself is caught at Python's recursion limit instead, and reported as the same fault. CircuitPython does not carry it: its playback layer already pulls natively from its own audio thread, and a second pull loop would be a second owner of the same graph. It still takes the lock and the hook table, on default hooks, which cost a load and a branch.

The hardware — the channel a board writes into, and a live microphone as a source — is not here. It arrives as a separate module, _audioif, from a platform driver, and audiopump.driver() tells you which one bound:

>>> audiopump.driver()
'esp32'          # or 'pthread', 'win32', 'none'

'none' is a real answer rather than a failure: the default hook table is all NULLs, which means one thread and no hardware, and that is exactly how the WebAssembly build runs. Writing a driver for a platform that has none yet is docs/pump-ports.md.

Status

MicroPython: all module tiers ported and oracle-diffed byte-for-byte against CircuitPython's own unix build; DSP parity re-verified on windows and wasm; built and measured on two real mcu targets, ESP32-P4 (hardware-confirmed by ear) and RP2040 (build-only). See docs/porting-plan.md for the full phased history and docs/upstream-sync.md for every deliberate difference from upstream CircuitPython and how the port is kept in step with it.

CPython: the public surface and wheel plumbing are present, and the committed synthesis, mixer, MIDI, streaming, and effects fixtures match CircuitPython 11.0.0-alpha.1 PCM byte-for-byte — built at the same voice ceiling this port ships, which is the only comparison worth making. Import/API smoke success is not used as a substitute for those comparisons.

mp3 (the Adafruit_MP3/Helix decoder audiomp3 depends on, RPSL/RCSL-licensed — not MIT, carried unmodified per upstream's own terms) is fetched by scripts/fetch_deps.sh at the commit DEPENDENCIES.lock pins, not vendored into this repo.

The playback-facing pull protocol (audiocore.get_buffer/reset_buffer) is consumed by pydevices' lib/audiodev package (AudioOut in sample_out.py), which pumps any audiosample into any of audiodev's existing push-PCM transports (sdl2/win/wasm/i2s/emulated) — see that package's own docs for the playback-side contract this repo's protocol implementation is built to satisfy.

Acceptance target: todbot's synthtools running unmodified, with rendered PCM diffed against CircuitPython's own unix coverage build (which already contains the entire DSP stack — the parity oracle for this whole port).

What's planned next is in ROADMAP.md.

Sound stability

The API is our contract with you: class names, signatures, metadata, and macro surfaces stay stable and change only deliberately. The sound is not part of that contract for the instruments and effects built on it (the audiocomponents packages). They sound great, but they are not all as accurate as they could be, and implementations will keep being refined as the library matures — so a component may render audibly differently from one release to the next. If a composition depends on the exact sound of a release, pin that release rather than tracking the latest; the code of every release stays available for exactly this reason.

Beneath the components sits a harder guarantee: the audiodsp core — the CircuitPython-compatible synthio/audiocore/effects-module layer — is held bit-exact to CircuitPython itself, verified by parity gates, and that never changes release to release. Where we find CircuitPython and audiodsp disagree, we treat it as a bug and report it upstream. The components are where the sound evolves; the floor they stand on does not.

License

audiodsp is MIT licensed — see LICENSE.

The attribution that goes with it lives in NOTICE: substantial portions of this code are ported from CircuitPython under its own MIT license, CircuitPython is a trademark of Adafruit Industries and this project is not affiliated with or endorsed by them, and the vendored test corpus under tests/vendor/ carries its own licenses. The two are separate files on purpose — GitHub and PyPI only detect the license when LICENSE holds the MIT text and nothing else, so please do not fold NOTICE back into it. NOTICE is in setuptools' default license-files glob, so it ships in the wheel and sdist alongside LICENSE.

The Helix MP3 decoder that audiomp3 wraps is RPSL/RCSL-licensed, not MIT; scripts/fetch_deps.sh fetches it; it is not part of this repository. See docs/upstream-diff.md.

The Tremor Vorbis decoder that audiovorbis wraps is in this repository, in third_party/tremor, under Xiph.Org's BSD-3-Clause licence (COPYING), which travels with the wheel.

Metadata

Release files for pydevices-audiodsp 0.6.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for pydevices-audiodsp 0.6.6
File
pydevices_audiodsp-0.6.6-cp314-cp314-win_amd64.whl CPython 3.14 CPython 3.14 Windows x86-64 Details
pydevices_audiodsp-0.6.6-cp314-cp314-pyemscripten_2026_0_wasm32.whl CPython 3.14 CPython 3.14 PyEmscripten 2026.0+ WebAssembly Details
pydevices_audiodsp-0.6.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
pydevices_audiodsp-0.6.6-cp314-cp314-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 macOS 11.0+ ARM64 Details
pydevices_audiodsp-0.6.6-cp314-cp314-android_21_x86_64.whl CPython 3.14 CPython 3.14 Android API level 21+ x86-64 Details
pydevices_audiodsp-0.6.6-cp314-cp314-android_21_arm64_v8a.whl CPython 3.14 CPython 3.14 Android API level 21+ ARM64 v8a Details
pydevices_audiodsp-0.6.6-cp313-cp313-win_amd64.whl CPython 3.13 CPython 3.13 Windows x86-64 Details
pydevices_audiodsp-0.6.6-cp313-cp313-pyemscripten_2025_0_wasm32.whl CPython 3.13 CPython 3.13 PyEmscripten 2025.0+ WebAssembly Details
pydevices_audiodsp-0.6.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
pydevices_audiodsp-0.6.6-cp313-cp313-macosx_11_0_arm64.whl CPython 3.13 CPython 3.13 macOS 11.0+ ARM64 Details
pydevices_audiodsp-0.6.6-cp313-cp313-android_21_x86_64.whl CPython 3.13 CPython 3.13 Android API level 21+ x86-64 Details
pydevices_audiodsp-0.6.6-cp313-cp313-android_21_arm64_v8a.whl CPython 3.13 CPython 3.13 Android API level 21+ ARM64 v8a Details
pydevices_audiodsp-0.6.6-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details
pydevices_audiodsp-0.6.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
pydevices_audiodsp-0.6.6-cp312-cp312-macosx_11_0_arm64.whl CPython 3.12 CPython 3.12 macOS 11.0+ ARM64 Details
pydevices_audiodsp-0.6.6-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details
pydevices_audiodsp-0.6.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
pydevices_audiodsp-0.6.6-cp311-cp311-macosx_11_0_arm64.whl CPython 3.11 CPython 3.11 macOS 11.0+ ARM64 Details

Total release size: 6.8 MB

Release files / pydevices_audiodsp-0.6.6-cp314-cp314-win_amd64.whl

Download URL pydevices_audiodsp-0.6.6-cp314-cp314-win_amd64.whl
Size 288.2 kB
Tags CPython 3.14 Windows x86-64
SHA-256 checksum
How to use checksums
79bc9ee1d25e3efebc59501fd508a1fd7b6389026dda1eb4118d3d7603dc773a
BLAKE2b-256 checksum
How to use checksums
dc579661cdb25e200f007205863bc420ad561a25c53d18f085f012376cd5107d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp314-cp314-pyemscripten_2026_0_wasm32.whl

Download URL pydevices_audiodsp-0.6.6-cp314-cp314-pyemscripten_2026_0_wasm32.whl
Size 231.5 kB
Tags CPython 3.14 PyEmscripten 2026.0+ WebAssembly
SHA-256 checksum
How to use checksums
d8e5ee9c88d46b6d0ffbcadc03fb2123021e642e70f3b1e83e0c3f4f72d2774d
BLAKE2b-256 checksum
How to use checksums
7de8fdf0b0571b3744481b366b278aa2273de14f3f831cf65fe5f3cd7c1b8557
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL pydevices_audiodsp-0.6.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 681.1 kB
Tags CPython 3.14 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
4a2ad593e954704893086241ac47da50a50904f29ba23c31e8de02e42b17e6de
BLAKE2b-256 checksum
How to use checksums
5759ae0411e002e466f75ce73bc530e29be113f95bdabded46dd5091eaf10f62
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp314-cp314-macosx_11_0_arm64.whl

Download URL pydevices_audiodsp-0.6.6-cp314-cp314-macosx_11_0_arm64.whl
Size 304.3 kB
Tags CPython 3.14 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
6c4652b65d4fa186ab73ca0047cdc2f3d8e0ea6e1e85718ebfdf1637da8f0012
BLAKE2b-256 checksum
How to use checksums
00b629a5e36b1fcfd7e67d7607dd34b07e46e42cba9513e711592c638147a704
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp314-cp314-android_21_x86_64.whl

Download URL pydevices_audiodsp-0.6.6-cp314-cp314-android_21_x86_64.whl
Size 325.3 kB
Tags Android API level 21+ x86-64 CPython 3.14
SHA-256 checksum
How to use checksums
062ec7297d4e7b20f18c7efff221e07d336e861fec2d8711a24ae431c1e449cd
BLAKE2b-256 checksum
How to use checksums
762d3f921652db74931ca612f1f7584c9e0e9c907b5e97c157804aa9a1c0aab9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp314-cp314-android_21_arm64_v8a.whl

Download URL pydevices_audiodsp-0.6.6-cp314-cp314-android_21_arm64_v8a.whl
Size 313.1 kB
Tags Android API level 21+ ARM64 v8a CPython 3.14
SHA-256 checksum
How to use checksums
ca88b5ec24c6636dff3fec7a892231dea14a2d3cc1259425cf748b1f5c6c767e
BLAKE2b-256 checksum
How to use checksums
ff81b3c6aeb0bd6c62a0706713c3a14da4db31cf8d477d209fadbe6a6ed65138
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp313-cp313-win_amd64.whl

Download URL pydevices_audiodsp-0.6.6-cp313-cp313-win_amd64.whl
Size 284.6 kB
Tags CPython 3.13 Windows x86-64
SHA-256 checksum
How to use checksums
5be06295383f252cf2ef6a0fae0b77036f99107a1451e3f6733e7fda1a0eff68
BLAKE2b-256 checksum
How to use checksums
fd052f0472e348bac8d54868ad31f6ce3e0a556cac3d548cb881f53f6ce905c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp313-cp313-pyemscripten_2025_0_wasm32.whl

Download URL pydevices_audiodsp-0.6.6-cp313-cp313-pyemscripten_2025_0_wasm32.whl
Size 231.6 kB
Tags CPython 3.13 PyEmscripten 2025.0+ WebAssembly
SHA-256 checksum
How to use checksums
7eacc5566864192bb2b182dfbe1e95476888715f3d816c921c01ee93c3d4e1a8
BLAKE2b-256 checksum
How to use checksums
f663c594ce572f22fe99eddce613bb495f214aed72272f5933824994e16ffd5f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL pydevices_audiodsp-0.6.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 681.2 kB
Tags CPython 3.13 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
e745632ea2a04b7967597273f007bac52c531761830ab63f87468b6b239a83dd
BLAKE2b-256 checksum
How to use checksums
1905924271cd97ba7a243802e4f85c934eb60af374f6bcb65476bdb906bfafb6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp313-cp313-macosx_11_0_arm64.whl

Download URL pydevices_audiodsp-0.6.6-cp313-cp313-macosx_11_0_arm64.whl
Size 304.3 kB
Tags CPython 3.13 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
00491d34d22ac7b39800a0de51514dbe1a649d624a78bcbdee9822d0c525d5ce
BLAKE2b-256 checksum
How to use checksums
51d12cd91092c194a24dc47742d47195eea3e1e09c15f1226f7d07adb255a98e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp313-cp313-android_21_x86_64.whl

Download URL pydevices_audiodsp-0.6.6-cp313-cp313-android_21_x86_64.whl
Size 325.3 kB
Tags Android API level 21+ x86-64 CPython 3.13
SHA-256 checksum
How to use checksums
3809b4f1f9232caed15be36388a511920e041d0500d0582f035549a4f5d9b303
BLAKE2b-256 checksum
How to use checksums
482f8dcc18e079402cc38017454d43c84d4cf8dbe50fe9e2ac9e15c259bb4061
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp313-cp313-android_21_arm64_v8a.whl

Download URL pydevices_audiodsp-0.6.6-cp313-cp313-android_21_arm64_v8a.whl
Size 313.1 kB
Tags Android API level 21+ ARM64 v8a CPython 3.13
SHA-256 checksum
How to use checksums
33a60ca6b0fe2afe92a42a0c4c56e455f2aeca7860327a05bdbecd4b00c98cab
BLAKE2b-256 checksum
How to use checksums
ae2912d85f9d08570ddb857ed006d5f9ac271f2374dd74eca9d59afac58df5a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp312-cp312-win_amd64.whl

Download URL pydevices_audiodsp-0.6.6-cp312-cp312-win_amd64.whl
Size 284.6 kB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
0d0a85045893267fab51dbdfe19889e96127f7b559946fcf98b3a9dbc8400978
BLAKE2b-256 checksum
How to use checksums
a0a1350a42136663636628568a3cdb4a8b087dcdb7a403ce643f62f231226feb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL pydevices_audiodsp-0.6.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 681.2 kB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
7477b9aafd3eaf5c9f7f1c0d73bb5c130009a17cafd0d701a88853876aac4f06
BLAKE2b-256 checksum
How to use checksums
62fc0f045e918025befae22b3df483c6c031292b50d12a623d87e643da559acf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp312-cp312-macosx_11_0_arm64.whl

Download URL pydevices_audiodsp-0.6.6-cp312-cp312-macosx_11_0_arm64.whl
Size 304.4 kB
Tags CPython 3.12 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
8fdec1110f397cb9b9a2a8c78dd14a6a0a3dd4a590cff1af2671a9869dd4f71f
BLAKE2b-256 checksum
How to use checksums
6af7469934a1f6a3a6340b76a0df1632c2cc28851d992d3b4142fda18a61df06
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp311-cp311-win_amd64.whl

Download URL pydevices_audiodsp-0.6.6-cp311-cp311-win_amd64.whl
Size 284.7 kB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
7505c324c9e50b5dd624ffff051915ca466ce8cdee30599a7cd224651f37a21f
BLAKE2b-256 checksum
How to use checksums
09efdd5ca9f93d29fcc8e566dbeaf276a0cbe2d3297805ef09245b1c013289f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL pydevices_audiodsp-0.6.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 681.6 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
8301d743c8e5f606544ee8096fbec90cc89fc93f07f0074f25995a018c6f3b39
BLAKE2b-256 checksum
How to use checksums
65d78fd03af9996844e67be6fc542e41e375721344c3902fbcbc1680f23da9ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / pydevices_audiodsp-0.6.6-cp311-cp311-macosx_11_0_arm64.whl

Download URL pydevices_audiodsp-0.6.6-cp311-cp311-macosx_11_0_arm64.whl
Size 305.8 kB
Tags CPython 3.11 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
d4d212d96f5b91f70955a91738f5ddd93fb1b47c9f8be4c3f6744b8d054e44dd
BLAKE2b-256 checksum
How to use checksums
08320998f24f27df0cc676eb9ee9463e33f26cde611dbb2d85e2af1385687df3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.7

24 release files

This release

0.6.6 This release

18 release files

0.6.5

18 release files

0.6.3

18 release files

0.6.2

18 release files

0.6.1

18 release files

0.6.0

18 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page