Improv Audio Software
This is a continuation of the original Mr. Voice Perl/Tk app, circa 2000. While still in use in many CSz clubs, the Perl/Tk app was showing its age, and nearly impossible to build correctly. This caused problems on more modern operating systems.
Thus, 20 years later, this project to rewrite the software in a more modern way.
Mx. Voice depends on Node.js. Requires Node.js 22.12.0 or higher (22.18.0 recommended). The project uses mise for version management - see .mise.toml.
Check out the source code from Github. Go into the mxvoice-electron folder.
The first time that you run the software in development mode, you will need to install the dependencies. Do that by running:
npm installThis should install any required node modules in the node_modules subdirectory. Please report any problems installing dependencies.
Once your node dependencies are installed, you can run the currently-checked-out code in development mode with:
npm startThat should launch the app onto your desktop!
This project uses Playwright with first-class Electron support. Each test suite (file) launches the real app in a fully isolated environment.
🎉 Comprehensive Test Coverage: The test suite covers 100% of major application functionality including search, song management, bulk operations, categories, audio playback, holding tank, hotkeys, UI controls, preferences, and system integration.
Important: On macOS/Linux, unset ELECTRON_RUN_AS_NODE before running tests:
# Required unit/policy checks used by CI
npm run build:preload
npm run test:no-skips
npm run test:unit:coverage
# Run all tests (comprehensive E2E coverage)
unset ELECTRON_RUN_AS_NODE && npm test
# Interactive test runner UI
unset ELECTRON_RUN_AS_NODE && npm run test:ui
# Headed mode (see windows during tests)
unset ELECTRON_RUN_AS_NODE && npm run test:headed
# Debug mode
unset ELECTRON_RUN_AS_NODE && npm run test:debug
# View the HTML report from the last run
npm run test:report
# Optional: manual smoke test (excluded from default runs)
unset ELECTRON_RUN_AS_NODE && npx playwright test tests/e2e/smoke.spec.jsCI treats unit coverage and Electron E2E as independent required gates. Skipped or fixme tests are not permitted; make the test deterministic or replace it with a lower-level contract test. Hardware-specific measurements such as audio RMS may be conditional, but their state and UI assertions must continue to run. Coverage thresholds live in vitest.config.js and should increase as coverage improves.
Why? IDEs like VS Code and Cursor often set ELECTRON_RUN_AS_NODE=1, which prevents Electron from launching its GUI. Tests require the full GUI application.
For details on test isolation, per‑suite environments, CI behavior, and complete coverage breakdown, see tests/README.md and tests/ELECTRON_TESTING_GUIDE.md.
The application uses an intelligent routing system when importing multiple audio files. You can tune the "Middle Path" threshold in:
src/renderer/modules/bulk-operations/multi-song-import.js->MULTI_SONG_THRESHOLD(Default:20)
Routing Logic:
- 1 Song: Individual Add Modal (manual metadata entry).
- 2 to N Songs: Multi-Song Import Modal (scrollable list with per-song metadata fine-tuning).
- > N Songs: Traditional Bulk Add Modal (assigns one category to all files).
The app follows a modern Electron architecture with context isolation enabled and a modular codebase:
-
src/main/(Main process)- Entry:
src/main/index-modular.js - Modules:
app-setup,ipc-handlers,file-operations,debug-log - Responsibilities: create window/menu, initialize store/DB, secure IPC, auto-updater
- See
src/main/README.md
- Entry:
-
src/preload/(Preload, sandboxed + context isolated)- Source:
src/preload/preload-modular.cjs(bundled topreload-bundle.cjsby esbuild) - Exposes vetted APIs via
contextBridgeaswindow.secureElectronAPI(and a compatibilitywindow.electronAPI) - Bridges events from main to renderer
- See
src/preload/README.md
- Source:
-
src/renderer/(Renderer process)- Feature modules under
src/renderer/modules/(each with its own README) - Core infrastructure:
function-registry,event-manager,function-monitor,module-loader - Services facades over secure APIs:
src/renderer/services/
- Feature modules under
-
Bootstrap 5 integration via
modules/ui/bootstrap-adapter.js(no jQuery plugins). jQuery is not required; DOM helpers live inmodules/dom-utils/.- See
src/renderer/README.md
- See
- Sandbox: ON (preload bundled via esbuild, can only
require('electron')) - Context Isolation: ON
- No direct Node.js access in renderer; all privileged operations go through preload‑exposed secure APIs
- Named IPC operations only — no raw SQL or arbitrary queries from renderer
- IPC handlers validate inputs on the main side
- DOMPurify sanitization on dynamic HTML rendering
- Uses the
node-sqlite3-wasmWebAssembly module for cross-platform compatibility - No native binary dependencies - works consistently across all architectures (x64, ARM64)
- Database files are stored in the user's application data directory
- Automatic migration support from legacy database formats
- Fallback to in-memory database if file operations fail
- All code uses ES Modules (
import/export) - All imports must include
.jsextensions - See module READMEs for detailed patterns and requirements
- Assets:
src/assets/(seesrc/assets/README.md) - Styles:
src/stylesheets/(seesrc/stylesheets/README.md) - Tests:
tests/(seetests/README.md) - Docs:
docs/(see individual documentation files)
- Open DevTools from the app menu (View → Developer Tools)
- Main process logs via structured DebugLog; renderer logs via the DebugLog module
- Bootstrap 5 is bundled via
bootstrap.bundle.min.jsand accessed using the adapter; preferdata-bs-*attributes in HTML - Auto-updater logs show markdown processing status for release notes
- Use
npm test:headedto see the app during test execution
The following build scripts are available:
# Development
npm start # Start the app in development mode
# macOS Builds
npm run build:mac:universal # Build universal macOS app (x64 + ARM64)
npm run build:mac:arm64 # Build ARM64-only macOS app
# Windows Builds
npm run build:win # Build Windows installer
# Linux Builds
npm run build:linux # Build all Linux formats
npm run build:linux:deb # Build Debian package
npm run build:linux:appimage # Build AppImage
# Publishing (GitHub Actions)
npm run release:mac # Publish macOS stable build to GitHub
npm run release:mac:prerelease # Publish macOS prerelease to GitHub
npm run release:mac:draft # Publish macOS draft to GitHub
npm run release:win # Publish Windows stable build to GitHub
npm run release:win:prerelease # Publish Windows prerelease to GitHub
npm run release:win:draft # Publish Windows draft to GitHub
npm run release:linux # Publish Linux stable build to GitHub
npm run release:linux:prerelease # Publish Linux prerelease to GitHub
npm run release:linux:draft # Publish Linux draft to GitHub
# Local builds (no publishing)
npm pack # Build without publishing
npm run dist # Build all platforms locally
# Utility scripts
npm run lint # Run ESLint
npm run lint:fix # Run ESLint with auto-fixUniversal Mac builds (x64 + ARM64) are built locally:
npm run build:mac:universalThis creates universal builds (x64 + ARM64) in your dist/ directory. For ARM64-only builds:
npm run build:mac:arm64To release to GitHub:
-
Push a tag to trigger the release:
git tag v{version} git push origin v{version} -
Or use the release scripts directly:
npm run release:mac:prerelease # For prerelease builds npm run release:mac # For stable releases npm run release:mac:draft # For draft releases
The app now supports two auto-update providers:
- 4.x users: GitHub provider for multi-architecture support with automatic release notes processing
- 3.x users: Custom download server (legacy support)
Release Notes Processing:
- GitHub release notes are automatically processed from markdown to HTML
- Users see properly formatted update information with bullet points, paragraphs, and links
- Fallback to raw text if markdown processing fails
Update Channel Control:
- Users can choose whether to receive prerelease updates (beta/alpha versions)
- Default behavior: Only stable releases are shown
- Prerelease updates are offered if:
- User explicitly opts-in via Preferences → Update Options, OR
- User is currently running a pre-release version (e.g., 4.0.0-pre.4)
- Setting can be changed at any time and takes effect immediately
This ensures backward compatibility while providing modern auto-update functionality for new releases.
To build Linux packages:
# Build all Linux formats (AppImage and deb)
npm run build:linux
# Build specific formats
npm run build:linux:deb # Debian package
npm run build:linux:appimage # AppImageTo release to GitHub:
npm run release:linux:prerelease # For prerelease builds
npm run release:linux # For stable releases
npm run release:linux:draft # For draft releases- AppImage: Universal Linux format, runs on most distributions
- Debian (.deb): Package for Debian, Ubuntu, and derivatives
- Category: AudioVideo
- Dependencies: Listed in
package.jsonunderdeb.depends
To build and sign the Windows installer, use:
npm run build:winor
npm run build:winThis command will:
- Build the Windows installer (
.exe) in thedist/directory. - Automatically sign all executables and DLLs using Windows native signtool.exe with YubiKey certificate.
- Automatically calculate correct checksums in
latest.yml- no post-processing needed.
Before building, ensure you have:
- Windows SDK installed (includes signtool.exe) - auto-detected from common locations
- YubiKey with code signing certificate imported and accessible
- Optional environment variables (for customization):
SIGNTOOL_PATH: Custom path to signtool.exe (if not in standard location)YUBIKEY_CERT_NAME: Specific certificate name to use (optional, auto-selects if not provided)YUBIKEY_PIN: YubiKey PIN (optional, will prompt if not set - cached for session)
Example (PowerShell):
# Optional: specify custom signtool path
$env:SIGNTOOL_PATH="C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe"
# Optional: specify certificate name (if multiple certificates available)
$env:YUBIKEY_CERT_NAME="Your Certificate Name"
# Optional: provide PIN (otherwise will prompt once per session)
$env:YUBIKEY_PIN="your_pin"The signing process uses a two-phase approach to ensure all files are properly signed:
-
Pre-Installer Signing (
afterPackhook):- All files in
win-unpacked/(main executable, DLLs, etc.) are signed in ONE batch command - This happens BEFORE the installer is created, ensuring signed files are included in the installer
- One PIN prompt for all unpacked files
- All files in
-
Post-Installer Signing (
signAllWindows.cjs):- The installer and uninstaller are signed after creation
- One PIN prompt for installer/uninstaller
Key Benefits:
- Batch signing: All files signed in single signtool commands reduces PIN prompts from 10-12 to just 2
- Signed files in installer: Files inside the installer are properly signed (not unsigned copies)
- YubiKey smart card: Uses Windows native signtool.exe with YubiKey certificate from Windows certificate store
- Automatic checksums: electron-builder calculates correct checksums automatically - no post-processing needed
- Comprehensive coverage: All Windows artifacts are properly signed for consistent publisher trust
After building, you can verify the signature:
Using Windows Explorer:
- Right-click the installer in
dist/ - Select Properties > Digital Signatures
- Select the signature and click Details to view certificate info
Using signtool.exe (if available):
signtool verify /pa "C:\path\to\mxvoice-electron\dist\Mx. Voice Setup {version}.exe"You should see "Successfully verified" if the signature is valid.
To publish Windows releases, use your preferred workflow for uploading the signed installer to GitHub or your distribution platform.
Note: Signing uses Windows native signtool.exe with YubiKey smart card certificate. You'll be prompted for your YubiKey PIN twice during the build: once for unpacked files, and once for the installer/uninstaller.
- Database:
node-sqlite3-wasm- SQLite WebAssembly module for cross-platform database support - UI Framework: Bootstrap 5 with custom adapter (no jQuery required)
- Audio: Howler.js for audio playback, WaveSurfer.js for waveform visualization
- Hotkeys: Mousetrap for keyboard shortcut binding
- Guided Tours: Driver.js for "What's New" feature tours
- Auto-updates:
electron-updaterwith GitHub provider support - Audio Metadata:
music-metadatafor extracting audio file metadata - Logging: Structured DebugLog system with file rotation and export via
electron-log - Sanitization: DOMPurify for safe HTML rendering of release notes
- Analytics:
posthog-nodefor anonymous, opt-out product analytics (main-process only; disabled in dev/test unlessANALYTICS_ENABLED=1)
The package.json has been cleaned up to remove orphaned dependencies. All remaining packages are actively used:
- FontAwesome for UI icons
- Bootstrap for responsive UI components
- node-sqlite3-wasm for cross-platform database
- DOMPurify for HTML sanitization of release notes
- Audio libraries for playback and visualization
- Electron utilities for development and production builds
Helpful tools and documentation:
Mx. Voice 4 is brought to you with love by:
- Wade Minter (wade@wademinter.com)
- Andrew Berkowitz (andrew@andrewberkowitz.com)