A native macOS meeting notepad for Bahasa Indonesia, in the spirit of Granola. Type sparse notes during a call; Notula records both sides, transcribes them, answers questions mid-meeting, and turns your notes into finished minutes.
Download the latest release · 15 MB · Apple silicon
Built because three things about the alternatives did not work:
- Indonesian support is the point, not an afterthought. Apple's own
SpeechAnalyzerdoes not supportid_IDat all as of macOS 26. - You choose the model — local Whisper, ElevenLabs, Deepgram, OpenRouter, Ollama, or any OpenAI-compatible endpoint.
- Everything stays on your Mac. No account, no sync, no server. SQLite on disk, plus a Markdown mirror any editor can read.
SwiftUI and Swift 6. No Electron, no Node, no Python, no bundled web server — the whole app is a 15 MB binary.
- Records both sides of a call — a Core Audio process tap for system audio, the microphone for you. The two streams stay separate end to end, so "me" versus "them" is exact without any diarization.
- Transcribes as you go, so you can ask "what did I miss?" mid-meeting and get an answer covering only what you missed, not a summary of the whole call.
- Writes up the meeting from your sparse notes plus the transcript, in Bahasa Indonesia, keeping English technical terms as spoken.
- Mirrors everything to Markdown with YAML front-matter, so Obsidian, Bear and
grepall work on your meetings.
You type sparse notes during a call. Notula records both sides of the audio, transcribes it, lets you ask questions mid-meeting ("what did I miss?"), and afterwards merges your notes with the transcript into finished minutes.
Core Audio process tap ─┐
├─► 16 kHz mono ─► VAD ─► TranscriptionEngine ─┐
AVAudioEngine (mic) ────┘ │
▼
LiveContextBuffer ──► MeetingChat ("what did I miss?")
SQLite + FTS5 ──► Enhancer (final write-up)
The microphone and system-audio streams are kept separate end to end, which gives exact "me" vs "them" attribution without any diarization.
Open Xcode → Settings → Accounts → add your Apple ID. A free account works.
This is not optional and not cosmetic: an unsigned build gets no TCC prompt for audio capture and silently records silence. If your first recording is empty, this is why.
brew install xcodegen
./scripts/update-grdb.sh # vendors GRDB (see note below)
xcodegen generate
open Notula.xcodeprojGRDB is vendored under Vendor/ rather than resolved by SwiftPM. Its full git
history is ~225MB and cloning it failed repeatedly; a shallow clone of one tag
is 30MB and takes about 25 seconds. Vendor/ is gitignored and fully
reproducible from the script.
WhisperKit is a normal SwiftPM dependency and resolves from GitHub.
Whisper weights are downloaded on first use (~1GB for large-v3-turbo) into
Application Support — they are deliberately not bundled, which keeps the .app
itself small. Expect a wait on the first recording only.
macOS will ask for two separate permissions:
- Microphone — your side of the call.
- Audio Recording — the other side. This is a distinct TCC category
(
NSAudioCaptureUsageDescription), not the same as the microphone.
| Engine | Indonesian | Cost | Live Q&A | Audio leaves Mac |
|---|---|---|---|---|
| WhisperKit (default) | ~10–13% WER | free | yes | no |
| ElevenLabs Scribe v2 | ~3–5% WER | ~$0.40/hr | yes | yes |
| Deepgram Nova-3 | good, multilingual model | ~$0.35/hr | yes | yes |
| OpenAI-compatible | varies | varies | no | yes |
The default is local, so the app is private and free unless you opt out. The cloud engines are meaningfully more accurate on Indonesian — that is a real trade, offered rather than made on your behalf.
The batch OpenAI-compatible engine returns text only after recording stops, so mid-meeting Q&A is unavailable with it. The Ask panel disables itself and says so.
Apple's own SpeechAnalyzer is not an option here: as of macOS 26 its
supported locales include ms_MY but not id_ID.
- The language is forced, not detected. Whisper picks a language from roughly
the first 30 seconds, so a meeting that opens with English small talk gets
transcribed as English throughout. Default is
id. - English technical terms are preserved. Indonesian tech meetings code-switch
constantly ("nanti kita deploy dulu ke staging"); summaries keep
deploy,staging,pull requestas spoken rather than translating them into phrases nobody says. - Glossary for names and jargon, applied twice: as a decoder bias, and as a deterministic correction pass afterwards. The correction pass is the reliable half — there is an open WhisperKit bug (argmax-oss-swift #372) where prompt biasing can return an empty transcript, so the engine retries without biasing if that happens.
- Indonesian-native templates: Notulen Rapat, Standup Harian, Catatan Klien.
./scripts/make-dmg.sh # Release build -> build/Notula-<version>.dmg
swift tools/make-icon.swift Notula/Resources # regenerate the icon
iconutil -c icns Notula/Resources/AppIcon.iconset -o Notula/Resources/AppIcon.icnsThe icon is generated from tools/make-icon.swift rather than stored as a
binary asset, so it stays diffable and can be adjusted without a design tool.
The DMG will not open on other Macs without a workaround. Distribution
outside the App Store needs a Developer ID certificate and notarization, both of
which require the paid Apple Developer Program. See DISTRIBUTION.md.
xcodegen generate # after adding or removing files
xcodebuild -project Notula.xcodeproj -scheme Notula build
xcodebuild -project Notula.xcodeproj -scheme Notula testxcodegen generate enumerates sources at generation time, so re-run it whenever
you add a file or the new file will silently not be compiled.
A full build compiles WhisperKit and takes minutes. Most of the codebase depends only on system frameworks and can be checked in seconds:
find Notula -name '*.swift' ! -name 'WhisperKitEngine.swift' -print0 \
| xargs -0 swiftc -typecheck -swift-version 6 \
-target arm64-apple-macosx14.4 -sdk "$(xcrun --show-sdk-path --sdk macosx)"| Path | What |
|---|---|
Notula/Audio/ |
Process tap, mic, resampling, VAD |
Notula/Transcription/ |
Engine protocol and the four implementations |
Notula/LLM/ |
Chat completions client, live context, chat, enhancer |
Notula/Storage/ |
SQLite schema, records, settings, Keychain |
Notula/Indonesian/ |
Language profile, glossary, templates |
Notula/UI/ |
SwiftUI views |