A fast, async Emacspeak speech server for macOS written in Swift.
Requirements: Apple Silicon Mac (M1, M2, M3, M4, or newer)
- Installation - Get started with Homebrew or binary releases
- Helper Scripts and Tools - Voice browsers, device finders, configuration helpers
- Multi-Device Routing - Route audio to different devices/channels
- Troubleshooting - Common issues and solutions
- Contributing - Development setup, build instructions, release process
The easiest installation method for macOS users:
brew tap robertmeta/swiftmac
brew install swiftmacThen configure Emacs:
(setenv "EMACSPEAK_DIR" "/path/to/.emacspeak")
(load-file "/opt/homebrew/share/emacs/site-lisp/swiftmac-voices.el")
(dtk-select-server "swiftmac")For manual Emacspeak integration:
ln -sf /opt/homebrew/opt/swiftmac/libexec/swiftmac $EMACSPEAK_DIR/servers/
ln -sf /opt/homebrew/opt/swiftmac/libexec/log-swiftmac $EMACSPEAK_DIR/servers/
ln -sf /opt/homebrew/opt/swiftmac/libexec/cloud-swiftmac $EMACSPEAK_DIR/servers/
ln -sf /opt/homebrew/opt/swiftmac/libexec/ogg.framework $EMACSPEAK_DIR/servers/
ln -sf /opt/homebrew/opt/swiftmac/libexec/vorbis.framework $EMACSPEAK_DIR/servers/Download from GitHub releases.
Copy the extracted files to the $EMACSPEAK_DIR/servers and then add the swiftmac server to your emacs config, for more info see: Readme.emacspeak.org
See: Readme.emacspeak.org
SwiftMac includes several helper tools to make configuration easier.
Interactive Emacs tool to sample and test all installed voices.
Load it in your Emacs config:
(load-file "/path/to/swiftmac/sample-voices.el")Run M-x swiftmac-sample-voices to open an interactive buffer showing:
- All voices grouped by quality (Premium/Enhanced/Compact)
- Organized by locale (en_US, en_GB, etc.)
Keybindings:
RETors- Sample the voice at pointx- Stop all speakingg- Refresh the listq- Quit
Lists all available premium and enhanced voices with installation instructions.
./scripts/download-premium-voices.sh # Show available voices
./scripts/download-premium-voices.sh verify # Verify installed voicesTo download voices:
- Open System Settings → Accessibility → Spoken Content
- Click the INFO BUTTON (ⓘ) next to ‘System Voice’
- Search for ‘premium’ or ‘enhanced’
- Download only the voices you’ll use (each is 50-300MB)
Quick script to list all available voices with quality levels:
swift scripts/show-voices.swiftOutput includes language, identifier, name, and quality level (1=Compact, 2=Enhanced, 3=Premium).
**Default Configuration (swiftmac-voices.el)**
The swiftmac-voices.el file is the standard voice configuration that ships with Emacspeak. It provides:
- Default voice definitions
- CSS voice mapping (for rich text rendering)
- Integration with Emacspeak’s voice lock system
This file is automatically loaded when you select the swiftmac server.
**English (Premium + Enhanced)**
The repo includes examples/voices-config-english.el which uses:
- Samantha (Enhanced) as main/default voice
- Alex (Enhanced) for strings and quoted text
- Premium voices for special emphasis
(load-file "/path/to/swiftmac/examples/voices-config-english.el")**Polish**
The repo includes examples/voices-config-polish.el for Polish voices:
(load-file "/path/to/swiftmac/examples/voices-config-polish.el")**Markdown Reading**
The examples/markdown-voices-config.el provides optimized voice settings for reading Markdown files.
**Custom Voice Configuration**
Example of defining your own voices:
(swiftmac-define-voice voice-bolden "[{voice en-US:Evan}] [[pitch 1]]")
(swiftmac-define-voice voice-animate "[{voice en-US:Ava (Premium)}] [[pitch 1]]")
(swiftmac-define-voice voice-lighten "[{voice en-AU:Matilda}] [[pitch 1.1]]")
(swiftmac-define-voice voice-smoothen "[{voice en-US:Zoe (Premium)}] [[pitch 0.8]]")Control volume levels for different audio types (values between 0.0 and 1.0):
(setenv "SWIFTMAC_TONE_VOLUME" "0.1") ; Beeps and tones
(setenv "SWIFTMAC_SOUND_VOLUME" "0.1") ; Sound effects
(setenv "SWIFTMAC_VOICE_VOLUME" "1.0") ; SpeechLists all available audio output devices with their IDs and channel counts:
swift scripts/list-audio-devices.swift
# or
make list-devicesExample output:
[DEFAULT] DeviceID: 125 | "Audioengine D1" | Channels: 2
DeviceID: 110 | "LG Ultra HD" | Channels: 2
DeviceID: 89 | "MacBook Pro Speakers" | Channels: 2
Use these device IDs for multi-device routing configuration (see below).
The examples/ directory contains several example configurations:
examples/init.el-example.el- Example Emacspeak initializationexamples/minimal-emacspeak-init.el- Minimal working Emacspeak configexamples/voices-config-english.el- English voice configuration (Samantha + Alex)examples/voices-config-polish.el- Polish voice configurationexamples/markdown-voices-config.el- Voice configuration for reading Markdown filesexamples/simple-device-test.el- Basic device routing testexamples/simple-device-switch.el- Example of switching audio devicesexamples/device-test-with-stop.el- Device testing with stop commands
These are useful as starting points for your own configuration.
If you’re developing or debugging SwiftMac:
**Shell Test Scripts:**
scripts/test-clean-emacspeak.sh- Test with clean Emacspeak environmentscripts/test-dual-mode.sh- Test dual-process modescripts/test-multi-device.sh- Test multi-device routingscripts/test-multiple-instances.sh- Test multiple SwiftMac instancesscripts/test-proper-dual.sh- Test proper dual-mode setup
**Emacs Test Files:**
The tests/ directory contains automated test files:
tests/test-audio-routing.el- Audio routing teststests/test-notifications.el- Notification routing teststests/test-runtime-device-switching.el- Runtime device switch teststests/test-simple-routing.el- Simple routing tests
SwiftMac supports routing different audio types to different physical devices AND channels. All four audio types can be independently configured:
- Speech (main spoken output)
- Notifications (auditory icons/beeps)
- Tones (generated tones)
- Sound effects (played audio files)
Run this to list available audio output devices:
make list-devicesExample output:
[DEFAULT] DeviceID: 125 | "Audioengine D1" | Channels: 2
DeviceID: 110 | "LG Ultra HD" | Channels: 2
DeviceID: 89 | "MacBook Pro Speakers" | Channels: 2
Set environment variables in your Emacs init file (BEFORE loading emacspeak):
(setenv "SWIFTMAC_<TYPE>_DEVICE_AND_CHANNEL" "<deviceID>:<channel>")Where:
<TYPE>: SPEECH, NOTIFICATION, TONE, or SOUNDEFFECT<deviceID>: Numeric ID frommake list-devices(or 0 for system default)<channel>: left, right, or both
; Everything to Audioengine D1, stereo
(setenv "SWIFTMAC_SPEECH_DEVICE_AND_CHANNEL" "125:both")
(setenv "SWIFTMAC_NOTIFICATION_DEVICE_AND_CHANNEL" "125:both")
(setenv "SWIFTMAC_TONE_DEVICE_AND_CHANNEL" "125:both")
(setenv "SWIFTMAC_SOUNDEFFECT_DEVICE_AND_CHANNEL" "125:both"); Speech to headphones, notifications to monitor speakers
(setenv "SWIFTMAC_SPEECH_DEVICE_AND_CHANNEL" "125:both") ; Audioengine D1
(setenv "SWIFTMAC_NOTIFICATION_DEVICE_AND_CHANNEL" "110:both") ; LG Ultra HD
(setenv "SWIFTMAC_TONE_DEVICE_AND_CHANNEL" "125:both")
(setenv "SWIFTMAC_SOUNDEFFECT_DEVICE_AND_CHANNEL" "110:both")
; IMPORTANT: Enable notification server for multi-device routing
(setopt tts-notification-device "left"); Speech in both ears, notifications in left ear only
(setenv "SWIFTMAC_SPEECH_DEVICE_AND_CHANNEL" "125:both")
(setenv "SWIFTMAC_NOTIFICATION_DEVICE_AND_CHANNEL" "125:left")
(setenv "SWIFTMAC_TONE_DEVICE_AND_CHANNEL" "125:both")
(setenv "SWIFTMAC_SOUNDEFFECT_DEVICE_AND_CHANNEL" "125:left")
(setopt tts-notification-device "left") ; Enable notification server; Maximum separation for complex audio setups
(setenv "SWIFTMAC_SPEECH_DEVICE_AND_CHANNEL" "125:both") ; Speech -> Audioengine D1
(setenv "SWIFTMAC_NOTIFICATION_DEVICE_AND_CHANNEL" "110:left") ; Notifications -> LG left
(setenv "SWIFTMAC_TONE_DEVICE_AND_CHANNEL" "89:both") ; Tones -> MacBook speakers
(setenv "SWIFTMAC_SOUNDEFFECT_DEVICE_AND_CHANNEL" "110:right") ; Sounds -> LG right
(setopt tts-notification-device "left") ; Enable notification serverChange ONLY the channel (not device) on the fly:
tts_set_speech_channel {left|right|both}tts_set_notification_channel {left|right|both}
Note: Runtime device switching (changing the physical device) is not yet supported. Restart Emacs to change devices.
Emacspeak spawns two separate swiftmac processes:
- Speech process (main): Handles regular speech output
- Notification process: Handles auditory icons and notifications
The tts-notification-device setting triggers Emacspeak to spawn the second process with SWIFTMAC_AUDIO_TARGET set, which swiftmac detects as notification mode.
Each process reads its own device configuration from the environment variables, allowing speech and notifications to output to different physical devices.
For more details, see MULTI-DEVICE-SETUP.md.
This is an emacspeak server written in swift intended to be as async as reasonable, fast and responsive.
Unless you are a developer or interested in becoming one, you probably want to use the version bundled with emacspeak, I keep that copy up to date with this one fairly consistently.
Want to contribute or build from source? See Contributing.org for:
- Development requirements (Xcode, Homebrew tools)
- Build instructions and workflow
- Make commands reference
- Testing and debugging
- Release process
- Code style guidelines
Quick start for developers:
make debug # Build debug version
make test-emacs # Test with Emacspeak
make tidy # Format code (required before commits)I am aware of the current warnings, it is a goal to get it to build completely clean but tthat is not a priority right now, getting to v2 is the priority.
If you are hearing stuff twice, ensure that mac-ignore-accessibility is set and your emacs version supports it. If that doesn’t work, you can use the VoiceOver Utility that comes with MacOS to create an activity for Emacs.app to turn off voiceover while in the Emacs window. This only works if you are using a windowed version of Emacs (not terminal version).
The server intends to provide primitives to support all the features used by emacspeak and swiftmac-voices.el.
These are the commands sent via stdin and on thier own line.
a: queue audio iconc: queue coded: dispatch queuel: instant letterp: instant audio iconq: queue speechs: stop all (confirm with list)_sh: queue silencet: queue tonetts_pause: instant pause speech engine (should stop?)tts_reset: queue reset (to defaults?)tts_resume: instant resume speech enginetts_say: instant say no additional tweakingtts_set_character_scale: queue char scale changetts_set_punctuations: queue punct changetts_set_speech_rate: queue rate changetts_split_caps: queue split caps changetts_sync_state: [decomp] queue multiple settings changeversion: [decomp] say tts version
Not Implemented yet:
set_next_lang:set_previous_lang:set_langset_preferred_lang
Engine Specific:
tts_exit: instant exittts_set_pitch_multiplier: .5 to 2 pitch multipliertts_set_sound_volume: 0 to 1 (1 being 100% sound volume)tts_set_tone_volume: 0 to 1 (1 being 100% tone volume)tts_set_voice: queue voice changetts_set_voice_volume: 0 to 1 (1 being 100% voice volume)
Broken:
tts_allcaps_beep: queue caps beep change, setting only works on typing input
These are converted by the preprocessor into tts_ commands.
- [*] - queue silence in place of this
- The server should be as dumb as possible.
- Major decisions should be configurable in lisp.
- System should be “usable by default” meaing: once it builds, it works.
- Server will depend on only clearly defined and checked at compile time things. No secret deps on command line tools.
