Note Reader CosyVoice Contributors460 downloadsPrivacy-first plugin that can read aloud notes and text-based PDFs with fast local processing; continue from a PDF selection or read only selected text.
Language: English | 简体中文
A privacy-first Obsidian desktop voice reader for Markdown notes and text-based PDFs. Use local CosyVoice by default, or explicitly opt in to Microsoft Edge online voice, Microsoft Azure Speech, or OpenRouter TTS.
0 for strict on-demand synthesis.Here, a text-based PDF means a PDF with selectable embedded text. Scanned or image-only PDFs need OCR first.
Current reader control panel, including scoped audio export actions:

Complete settings page, assembled from sequential views of the same page. Secret values, vault names, note content, local paths, and surrounding Obsidian UI are excluded:

Scores are relative to the experience provided by this plugin. More filled stars always mean more favorable: stronger privacy, easier setup, faster first-chunk startup, broader voice choice, less dependence on user-managed paid API quota, or better offline capability. ★★★★★ is the strongest rating and ★☆☆☆☆ is the weakest. They are usage guidance, not security, provider-policy, pricing, or latency guarantees.
| Engine | Privacy | Setup convenience | Startup speed | Voice choice | Quota friendliness | Offline |
|---|---|---|---|---|---|---|
| Local CosyVoice | ★★★★★ | ★★☆☆☆ | ★★★★☆ | ★★★☆☆ | ★★★★★ | ★★★★★ |
| Microsoft Edge online voice | ★★★☆☆ | ★★★★☆ | ★★★★★ | ★★★★★ | ★★★★★ | ★☆☆☆☆ |
| Microsoft Azure Speech | ★★★★☆ | ★★★☆☆ | ★★★★★ | ★★★★★ | ★★★★☆ | ★☆☆☆☆ |
| OpenRouter TTS | ★★★★☆ | ★★★★★ | ★★★★★ | ★★★★★ | ★★★☆☆ | ★☆☆☆☆ |
edge-tts package, which does not expose an explicit ZDR control or guarantee for plugin requests; Edge therefore remains below Azure and OpenRouter. Azure receives four stars because this plugin uses the real-time prebuilt-voice API and Microsoft states that its input text and output audio are not stored in Microsoft logs; one star is withheld because the text is still processed in Microsoft's cloud. OpenRouter also receives four stars because requests force provider.zdr = true, deny provider data collection, and fail instead of relaxing those restrictions when no eligible endpoint exists; it is not equivalent to local processing because text still passes through OpenRouter and an upstream provider, and account-level logging and data sharing should remain disabled for private content.edge-tts executable. Azure receives three stars because it also uses direct HTTPS and needs no helper executable, although it still requires an Azure Speech resource, cloud, region, and key. Local CosyVoice requires a local runtime, model, and wrapper.In practice, choose Local CosyVoice for sensitive or offline reading, OpenRouter for the shortest direct-API setup plus model choice and enforced ZDR routing, Edge when avoiding a user API key matters more than installing its helper program, and Azure when you already manage a Microsoft Speech resource.
First install and enable the plugin, then open Settings -> Community plugins -> Note and PDF Voice Reader. Only enable an online-processing switch after deciding that the selected service may receive the text you ask the plugin to read.
Speech engine to OpenRouter TTS, then enable Allow OpenRouter online processing.Obsidian SecretStorage, create or select a secret containing the key, and choose a model and compatible voice.Speech engine to Microsoft Azure Speech, enable Allow Azure online processing, and select the matching Azure cloud and region.Obsidian SecretStorage, then choose a voice preset or enter a valid Azure voice ID.pipx install edge-tts, open a new terminal, and confirm that edge-tts --help works.Speech engine to Microsoft Edge online voice, enable Allow Edge online processing, set Edge TTS executable to edge-tts or its absolute path, and choose a voice.edge-tts interface used here does not provide an explicit ZDR guarantee for plugin requests.Speech engine to Local CosyVoice and enter the wrapper's absolute path in CosyVoice script.Read selection for a short test. This mode needs no online-consent switch and the plugin itself keeps readable text on the device, but the configured wrapper remains part of your trust boundary.Voice Reader control panel.1x, 1.25x, 1.5x, 2x, 1.1x, 1.2x, 1.3x, and 1.4x.Local CosyVoice, Microsoft Edge online voice, Microsoft Azure Speech, or OpenRouter TTS in settings. Local CosyVoice is the default.200,400,800, with at most one future chunk synthesized early by default.[28], [28, 29], and [28-30] as spoken references while preserving unit labels such as [s] and [%].Restore defaults button for resetting all plugin settings.Math reading language setting:English is the default for public releases, for example $a_b$ -> a subscript b.Chinese keeps Chinese math words, for example $a_b$ -> a 下标 b.$|Y_{k,h}|$ into spoken words instead of sending raw vertical bars.Skip math skips short formulas as well as long formulas.\alpha -> alpha, \beta -> beta, and \pi -> pi.\leq, \times, and _.\textbf{...}, \mathbf{...}, and \boldsymbol{...}.\frac{a}{b} as a over b in English mode or a 分之 b in Chinese mode.By default, the plugin uses local TTS. In Local CosyVoice mode, the plugin itself does not send note or extracted PDF text to Microsoft, OpenAI, or another remote TTS service. The configured wrapper remains part of your trust boundary and may make its own network requests.
PDF extraction uses Obsidian's bundled PDF.js and Vault.readBinary; the PDF file itself is not uploaded by this feature. To support PDF selection commands, the plugin temporarily keeps the selection's page number, relative in-page coordinates, and up to 2,000 characters of locator text in memory only; none of this selection locator is saved to settings or diagnostic logs. When an online speech engine is selected and its consent is enabled, extracted PDF text chunks are transmitted under the same rules as note text. Scanned or image-only PDFs need OCR before the plugin can read them.
Remember reading position is off by default. If you enable it, data.json stores the file path, file timestamp, PDF page or speech-chunk index, update time, and a normalized text anchor capped at 180 characters. It does not store the complete note or PDF body. Use Clear saved reading positions to remove all saved anchors; disabling the setting stops future use and updates but does not silently delete existing history.
Edge, Azure, and OpenRouter are opt-in online modes. Edge passes each chunk to the configured edge-tts executable. Azure sends each chunk by HTTPS to the selected Azure Speech cloud and region. OpenRouter sends each chunk to OpenRouter and an eligible upstream TTS provider. The plugin will not start an online mode until its separate online-processing consent setting is enabled. OpenRouter consent permits that transmission only; it does not permit non-ZDR routing. By default, the plugin may synthesize the next chunk while the current chunk is playing, but it never prefetches more than one future chunk. Stopping early can therefore leave at most one prefetched chunk unused. Set prefetch to 0 for strict on-demand synthesis. Provider billing units vary, so this bounds avoidable work rather than guaranteeing a fixed cost reduction.
Audio export always requires a separate per-export acknowledgement before synthesis starts. For an entire PDF or a PDF export from selection, local extraction and selection matching finish before the confirmation appears. The dialog then shows the selected scope, cleaned readable character count, exact number of planned synthesis segments, and planned vault-relative save path. For an online engine, only readable text in that scope is sent through those sequential segments and may consume provider quota or incur charges. Temporary failures can trigger bounded retries, so the number of network attempts can exceed the planned segment count. Export does not prefetch playback-continuity chunks, creates no vault attachment until every segment and finalization succeed, and can be cancelled with Stop.
Temporary text and audio are stored in a vault-specific folder under the operating system temporary directory, not inside the Obsidian vault. With Clean temporary audio enabled, plaintext chunk files are removed immediately after synthesis, remaining session files are removed when reading ends or stops, and stale plugin-owned files plus the legacy in-vault cache are cleaned at startup. The deliberate exception is a post-synthesis export failure: completed audio segments are kept locally for the current plugin session so Retry merge only can reuse them without another TTS request. A successful retry, Clear temporary data, or unloading the plugin while cleanup is enabled removes them. Diagnostic logging is off by default; when enabled, it records only bounded failure metadata without note names, note text, or child-process output.
Azure and OpenRouter keys use Obsidian SecretStorage by default on Obsidian 1.11.4 or later. The plugin's data.json contains only the selected secret identifier, not the secret value. Obsidian documents SecretStorage as vault-specific local secret storage; it should not be described as a guaranteed operating-system credential manager or macOS Keychain integration. A one-line key file outside every vault remains available as a compatibility fallback, and existing key-file configurations retain that mode when upgraded. See the official Obsidian SecretStorage guide.
Microsoft states that its real-time text-to-speech API does not retain the submitted text or generated audio; the text is still transmitted to and processed by the selected Azure Speech service. Confirm the terms applicable to your cloud and subscription. See Azure Speech text-to-speech data privacy and security.
Every OpenRouter request forces provider.zdr: true and provider.data_collection: "deny"; when no endpoint satisfies those restrictions, synthesis fails rather than weakening the policy. OpenRouter states that prompt storage is opt-in by default, but account-level input/output logging or data-sharing settings can still change that behavior, and request metadata is retained. Keep those account settings disabled for private content. See OpenRouter data collection and Zero Data Retention.
edge-tts; Azure uses an official regional endpoint derived from the selected cloud and validated region; OpenRouter uses the fixed https://openrouter.ai/api/v1/audio/speech endpoint.edge-tts executable in Edge mode. Azure and OpenRouter modes do not launch shell commands.Platform-neutral text cleanup, semantic chunking, PDF coordinate ordering, reading-position anchors, and playback state live in note-reader-core. This desktop repository retains filesystem access, child processes, local CosyVoice, edge-tts, audio merging, and export handling. The separate note-reader-mobile plugin uses the same core without bundling desktop-only APIs or local executable calls.
Local CosyVoice: a working local CosyVoice setup and a PowerShell wrapper compatible with:cosyvoice-wrapper.ps1 -InputPath <txt> -OutputPath <wav> -Speed <speed>
A recommended script path is:
%LOCALAPPDATA%\note-reader-cosyvoice\cosyvoice-wrapper.ps1
For local CosyVoice installation, hardware guidance, OS-specific notes, and wrapper examples, see Local CosyVoice setup.
For Microsoft Edge online voice: install the edge-tts CLI and either make the command available on PATH or enter its absolute executable path in Edge TTS executable. The plugin calls it with --file, --write-media, --voice, and --rate.
edge-tts is a third-party Python package published on PyPI that calls Microsoft Edge's online text-to-speech service. It is not bundled with this plugin and is not a local voice model.
Recommended command-line-only install:
pipx install edge-tts
If pipx is not installed yet:
py -m pip install --user pipx
py -m pipx ensurepath
Then open a new PowerShell window and run pipx install edge-tts.
Alternative install if you manage Python packages directly:
py -m pip install --user edge-tts
After installation, open a new PowerShell window and verify that the command is available:
edge-tts --help
To list available voices:
edge-tts --list-voices
Then open Settings -> Note and PDF Voice Reader:
Speech engine to Microsoft Edge online voice.Allow Edge online processing.Edge TTS executable to either edge-tts or an absolute path to the executable.Speed if needed. The plugin converts this to the edge-tts --rate option.Common presets include zh-CN-XiaoxiaoNeural, zh-CN-YunxiNeural, zh-CN-YunyangNeural, en-US-JennyNeural, en-US-GuyNeural, and en-GB-SoniaNeural. Use edge-tts --list-voices for the complete list supported by your installed version.
If Obsidian cannot find edge-tts, use the absolute executable path in the plugin settings and fully restart Obsidian. Do not rely on an unrelated application's private virtual environment unless you intentionally trust and maintain that installation.
Privacy note: Edge mode sends each text chunk to Microsoft Edge TTS. Microsoft's Edge Read aloud privacy documentation says text and generated audio used for online conversion are deleted immediately after conversion, but the third-party edge-tts calling interface used by this plugin does not expose an explicit ZDR guarantee for plugin requests. Treat it as online processing without a guaranteed ZDR control, and keep Speech engine set to Local CosyVoice for private or sensitive notes. See User data and privacy in Microsoft Edge.
On Obsidian 1.11.4 or later, the recommended and default API key storage choice is Obsidian SecretStorage. Use the secret control on the plugin settings page to create or select a secret containing the raw API key. Only that secret's identifier is saved in this plugin's data.json; the key value remains in Obsidian's vault-specific local secret store.
For an older Obsidian release or an existing file-based setup, select External one-line key file. Create a plain-text file outside every Obsidian vault, put the key on its only non-empty line, and do not sync, commit, or share the file. An existing configuration with a key-file path is migrated to this compatibility mode automatically.
Azure mode uses the official real-time Speech REST endpoint and supports Azure public cloud and Azure China operated by 21Vianet. Create a Speech resource in the intended cloud, then note its region and one subscription key. The required HTTPS request, SSML body, authentication header, and audio output header follow Microsoft's text-to-speech REST API reference.
If you select the external key-file fallback, a suitable path is:
%LOCALAPPDATA%\note-reader-cosyvoice\azure-speech-key.txt
Then open Settings -> Note and PDF Voice Reader:
Speech engine to Microsoft Azure Speech.Allow Azure online processing.Azure public cloud or Azure China operated by 21Vianet.eastasia, southeastasia, chinaeast2, or chinanorth3.Obsidian SecretStorage and create/select the Azure key secret, or choose the external file option and enter its absolute path.Presets include Mandarin Chinese zh-CN-XiaoxiaoNeural, zh-CN-XiaoyiNeural, zh-CN-YunxiNeural, and zh-CN-YunyangNeural; Cantonese and Taiwanese Mandarin; common US English en-US-JennyNeural, en-US-GuyNeural, and en-US-AriaNeural; and common UK English en-GB-SoniaNeural and en-GB-RyanNeural. The default Edge and Azure voice is the UK male voice en-GB-RyanNeural, selected for restrained long-form and academic reading.
The plugin derives the HTTPS host from the validated region and selected cloud; it does not accept a free-form Azure endpoint. Azure China endpoint differences are documented in Azure Speech sovereign clouds. Check the current Azure Speech language and voice list if a voice is unavailable in your region.
OpenRouter exposes an OpenAI-compatible TTS endpoint that accepts text and returns raw MP3 or PCM audio. This plugin always requests MP3 and validates the HTTP status and Content-Type before saving it. See the official OpenRouter TTS documentation.
The plugin retries temporary 408, 425, 429, 500, 502, 503, and 504 responses and transient network failures up to three total attempts with short bounded delays. It does not retry credential, model, voice, privacy-policy, malformed-request, or unexpected-content errors. A final HTTP 502 therefore indicates that OpenRouter or its upstream provider remained unavailable after the limited retries, rather than normally indicating an unsupported text character.
Create a dedicated API key in OpenRouter API Keys. Use a low spending limit and an expiration date where appropriate. If you select the external key-file fallback, a suitable path is:
%LOCALAPPDATA%\note-reader-cosyvoice\openrouter-api-key.txt
Then open Settings -> Note and PDF Voice Reader:
Speech engine to OpenRouter TTS.Allow OpenRouter online processing.Obsidian SecretStorage and create/select the OpenRouter key secret, or choose the external file option and enter its absolute path.The overall default is hexgrad/kokoro-82m with the UK English male voice bm_george, selected for restrained long-form and academic reading. When a different built-in model is selected, the plugin now chooses an English male voice where the publisher explicitly identifies one: Microsoft MAI uses the US English male Ethan, while Gemini uses the informative Charon because Google does not publish fixed gender or US/UK accent labels for its voices. Voice IDs are model-specific and are not interchangeable. OpenRouter-listed IDs and Microsoft's official MAI catalog were checked on 2026-08-27:
microsoft/mai-voice-2-flash: defaults to Microsoft-published US English male en-US-Ethan:MAI-Voice-2-Flash and adds Microsoft-published US English and Mandarin voices as compatibility presets alongside the four IDs exposed by OpenRouter.microsoft/mai-voice-2: defaults to Microsoft-published US English male en-US-Ethan:MAI-Voice-2; additional US English male and Mandarin ShortNames are compatibility presets because OpenRouter may accept them even when its supported_voices metadata omits them.google/gemini-3.1-flash-tts-preview: defaults to informative Charon and offers 12 curated presets from the 30 voices exposed by OpenRouter. Google describes these multilingual voices by delivery style rather than fixed gender or US/UK accent, so the plugin does not label any Gemini preset as a confirmed male or accent-specific voice.hexgrad/kokoro-82m: 12 presets, with two voices in each requested group: Mandarin Chinese female, Mandarin Chinese male, US English female, US English male, UK English female, and UK English male.Kokoro is currently the built-in OpenRouter choice that explicitly provides both US and UK English male presets. Microsoft's published MAI catalog currently provides US English male voices but no UK English MAI voice. Compatibility presets can still fail if OpenRouter or its upstream endpoint rejects an ID that its metadata does not advertise; the custom voice field remains available for independently verified IDs.
The settings page shows a short characteristics note and only the presets for the selected model. Model, voice, and ZDR endpoint availability can change. OpenRouter's live speech + ZDR model API remains the source for advertised routing metadata, while Microsoft's MAI voice catalog supplies the official MAI ShortNames. The delivery-style names come from Google's Gemini TTS voice list, and Kokoro language/gender groups follow its upstream voice catalog. Custom model IDs remain available, but a model with no eligible ZDR endpoint returns an error because the plugin never relaxes its privacy routing rules.
This plugin does not download models. Plan storage for the local TTS runtime before installing a voice model:
2.5 GB for a 300M model to about 9 GB for a 0.5B CosyVoice3 model.10-20 GB for one model and 30 GB+ if you keep multiple models, source checkouts, Conda environments, and caches.The configured script can call another local TTS engine instead of CosyVoice if it follows the same wrapper contract: read UTF-8 text from -InputPath, write a valid WAV file to -OutputPath, accept -Speed, and exit non-zero with a clear error on failure. Check the other model's license, language coverage, audio format, speed controls, startup latency, and whether it sends text outside your machine or trusted local network.
The Edge, Azure, and OpenRouter online modes are separate from the local wrapper contract. They write temporary MP3 files and use their corresponding voice setting. OpenRouter may ignore Speed for models whose provider does not support that parameter.
Use Local chunk limits to balance local startup latency and synthesis stability:
30,60,90,120,160,200.40,80,120,160,280,320.80,140,220,320,480,640.Online chunk limits applies to both notes and PDFs in Edge, Azure, and OpenRouter modes. Its default is 200,400,800, which uses a shorter first request and longer later requests to balance startup time, continuity, and request count.
Online synthesis prefetch defaults to 1: while the current chunk is playing, the plugin may prepare the next chunk to improve continuity. It never prepares more than one future chunk, so stopping early can leave at most one prefetched request unused. Set it to 0 when avoiding every unused future request matters more than the pause between chunks.
Open voice reader controlsRead current note or PDF aloudExport audio from current note or PDFExport audio from the current note and insert itRetry pending audio export merge onlyResume reading current note or PDFRead current PDF aloudRead current PDF from selection aloudRead selection aloudRead from selection aloudPause or resume voice readingSeek backward 5 secondsSeek forward 5 secondsMove to previous reading chunkMove to next reading chunkStop voice readingOpen a Markdown note or text-based PDF and choose Export audio. A scope picker offers Entire document, Selected text only, and From selection to end; the latter two require an active text selection. For an entire PDF or a PDF export from selection, local parsing and reliable position matching run first. The subsequent confirmation reports the exact readable character count, selected engine, synthesis segment count, scope, and planned save path, and its checkbox must be selected before synthesis starts.
Local mode combines PCM WAV segments into one WAV file. Edge, Azure, and OpenRouter combine validated MP3 frames into one MP3 file. Audio export save location can use Obsidian's attachment folder (the default), the source file's folder, or a custom vault folder. Entire, selected, and remaining exports use filenames such as Note name - narration.mp3, Note name - selection narration.mp3, and Note name - continued narration.mp3, with a numeric suffix when needed. For Markdown, Export & insert audio embeds the result at the current cursor or appends it to the original note. PDF export saves an audio attachment only because a PDF cannot be edited to insert an Obsidian embed.
Export processes exactly the displayed chunks in sequence and does not perform playback prefetch. If synthesis fails or the task is stopped, no partial attachment is added. If every segment has already been synthesized but merging or attachment finalization fails, the control panel exposes Retry merge only; it reuses the kept local segments and makes no TTS API request. Starting another export is blocked until that retry succeeds or Clear temporary data discards the kept segments.
When the Voice Reader control panel is focused, Space pauses or resumes reading, and Left Arrow or Right Arrow seek backward or forward in 5-second steps while audio is available.
The triangle buttons beside the progress bar jump to the previous text chunk or the next text chunk. Already synthesized chunks are reused when possible; otherwise the target chunk is synthesized before playback.
The progress bar shows whole-reading progress across all chunks. While audio is playing, the bar can be clicked or dragged. Seeking is limited to the currently loaded audio chunk; dragging outside that chunk is clamped to the nearest point in the current chunk.
Open a PDF stored in the vault, then click Read file in the control panel or run a PDF-capable command. Text extraction happens locally page by page. Once enough text for the first configured chunk is available, synthesis and playback can begin while later pages continue parsing. The Stop button cancels parsing, playback, and outstanding synthesis requests.
To start at a specific position, select text in the PDF text layer and click Read from selection, or run Read current PDF from selection aloud. The plugin starts extraction on that page and combines the selection's relative page coordinates with text matching, so repeated wording in an abstract and a later column can be distinguished. Read selection reads only the selected PDF text. If coordinates are unavailable, text matching remains as a compatibility fallback; if neither locator can be matched, the plugin displays a notice and starts at the beginning of the selected page.
The PDF must contain selectable embedded text. Password-protected, damaged, scanned, or image-only files cannot be extracted; run OCR or unlock the file first. Version 0.4.0 and later use text coordinates to recognize common two-column pages and read each vertical band left column before right column, while treating full-width headings as boundaries. Unusual layouts, rotated text, sidebars, and complex tables can still require a manual selection start or a better-tagged source PDF.
If Remember reading position is enabled, use Resume file in the control panel or Resume reading current note or PDF in the command palette. PDF resume starts on the saved page and locates the short anchor again; Markdown resume locates the same normalized anchor and falls back to the nearest saved chunk if the note changed.
Source modules live under src/. Build and run all tests before publishing:
npm install
npm test
The build bundles src/main.js and its local modules into the single root main.js required by the Obsidian Community installer. obsidian remains an external runtime dependency supplied by the host application.
The install package contains only:
manifest.jsonmain.jsstyles.cssREADME.mdINSTALL.mdLICENSEIt intentionally excludes data.json, legacy cache/last-error.log files, system temporary data, secrets, and local test files.
MIT.