[ INTERNALS ]
How it is built
Yummi fits in five dependencies and none of them is heavy. The most unusual thing it does — capturing the audio of a single process — is done in plain ctypes, because the API has no Python library.
Layers
Only ui/ knows Tkinter and only audio/ knows PortAudio. That is why everything else runs in the tests with no sound card and no screen.
yummi/audio/devices.pyfinding and choosing devicesyummi/audio/loader.pydecoding, resampling, normalisingyummi/audio/player.pyplayback across outputs and the live bridgeyummi/audio/loopback.pyper-process capture (WASAPI process loopback)yummi/tts/synthesis engines, automatic fallback and cacheyummi/ui/the Tkinter window and the black-and-white themeyummi/library.pythe clip libraryyummi/files.pyscanning the audio folderyummi/config.pypreferences in JSONyummi/hotkeys.pyglobal shortcuts (written, switched off)
The five dependencies
numpy
the maths over the samples
sounddevice
the bridge to sound cards, through PortAudio
soundfile
decodes MP3 and friends straight from memory, via libsndfile
edge-tts
Microsoft's neural voices
gTTS
the backup, when the first one doesn't answer
ffmpeg, pydub and scipy were left behind in version 3. pydub had stopped working on Python 3.13, when the audioop module left the standard library.
Per-window capture
ActivateAudioInterfaceAsync
AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK
PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE
Windows exposes a process's audio through ActivateAudioInterfaceAsync, with process loopback parameters. It is a C and COM API with no Python binding: the interfaces are called through ctypes, by each method's position in the function table, and the object that receives the asynchronous result is built by hand in Python. Outside Windows the module imports fine and only the capture declines, with a message.
The choice is saved by executable name, not by process id: closing and reopening Brave does not lose the setting.
Where the files live
Nothing is written inside the repository. Preferences, clips and cache live in the user folder:
Deleting that folder returns the program to a freshly installed state, without uninstalling anything.
%APPDATA%\Yummi\ ├── config.json saídas, voz, pasta de áudios, origem da ponte ├── clipes/ a biblioteca + biblioteca.json └── cache/ as falas já sintetizadas
Tests
157 tests run on four Python versions on every push, with ruff in the same step. The ones that need a sound card are skipped on the server, and the interface is tested against a fake Tk root.
157
pytest
4
3.10 → 3.13
MIT
licence