Skip to content

Repository files navigation

Notula

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.

License: MIT Platform Swift

Download the latest release · 15 MB · Apple silicon

Built because three things about the alternatives did not work:

  1. Indonesian support is the point, not an afterthought. Apple's own SpeechAnalyzer does not support id_ID at all as of macOS 26.
  2. You choose the model — local Whisper, ElevenLabs, Deepgram, OpenRouter, Ollama, or any OpenAI-compatible endpoint.
  3. 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.

What it does

  • 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 grep all work on your meetings.

How it works

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.

Setup

1. Signing (required — do this first)

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.

2. Dependencies

brew install xcodegen
./scripts/update-grdb.sh     # vendors GRDB (see note below)
xcodegen generate
open Notula.xcodeproj

GRDB 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.

3. First run

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.

Choosing a transcription engine

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.

Indonesian handling

  • 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 request as 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.

Releasing

./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.icns

The 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.

Development

xcodegen generate                      # after adding or removing files
xcodebuild -project Notula.xcodeproj -scheme Notula build
xcodebuild -project Notula.xcodeproj -scheme Notula test

xcodegen generate enumerates sources at generation time, so re-run it whenever you add a file or the new file will silently not be compiled.

Fast type-checking

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

Layout

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

About

Native macOS meeting notepad for Bahasa Indonesia. Records both sides of a call, transcribes locally or in the cloud, answers "what did I miss?" mid-meeting, and writes up minutes. No account, no sync — SQLite and Markdown on disk. SwiftUI, no Electron.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages