Audio plugins

Audio plugins add narration providers as separate packages. SuiteCut loads the selected package in its narration worker. Built-in Kokoro remains the default.

Install one model family

Terminal
npm install --save-dev @suitecut/audio-vits

Use @suitecut/audio-vits for VITS and Piper. Other packages are@suitecut/audio-matcha, @suitecut/audio-kokoro-sherpa,@suitecut/audio-kitten, @suitecut/audio-zipvoice,@suitecut/audio-pocket, and @suitecut/audio-supertonic.

TypeScript
import { test } from 'suitecut'

test.use({
  suitecutAudioPlugins: [
    {
      provider: 'piper-amy',
      module: '@suitecut/audio-vits',
      options: {
        model: './models/piper/en_US-amy-low/en_US-amy-low.onnx',
        tokens: './models/piper/en_US-amy-low/tokens.txt',
        dataDir: './models/piper/en_US-amy-low/espeak-ng-data',
      },
    },
  ],
})

test('records Piper narration', async ({ page, suitecut }) => {
  await page.goto('https://example.com')
  await suitecut.narrate('The report is ready.', {
    provider: 'piper-amy',
    voice: 'default',
    speed: 1.1,
  })
})

Each package validates only its model family's options. The packages share one Sherpa runtime, which npm deduplicates when a project installs more than one family. The adapters never download models. Download the selected model separately and pass its local file paths in the plugin options. Sherpa publishes compatible files in its TTS model releases.

Listen and set the voice

Piper Amy

Package @suitecut/audio-vits. Model en_US-amy-low. Voicedefault. Speed 1. Sample text: "This is the Piper Amy voice, running through the VITS audio plugin."

Piper Amy has one speaker, so set voice: 'default'. For a multi-speaker model, use its numeric speaker ID as the voice, such as voice: '2', or give the ID a name inspeakerIds.

TypeScript
test.use({
  suitecutAudioPlugins: [
    {
      provider: 'vits-cast',
      module: '@suitecut/audio-vits',
      options: {
        model: './models/cast/model.onnx',
        tokens: './models/cast/tokens.txt',
        speakerIds: { narrator: 2 },
      },
    },
  ],
})

test('uses a named speaker', async ({ suitecut }) => {
  await suitecut.narrate('Welcome to the tour.', {
    provider: 'vits-cast',
    voice: 'narrator',
  })
})

Plain Playwright

Pass the same references as audioPlugins in record() ordefineSuiteCut(). The provider in narrate() must match the configured provider ID.

Write a plugin

Export one ESM plugin object from the package default entry. Its synthesize() method receives text, voice, speed, output path, and JSON options. It must write mono PCM WAV before it resolves.

TypeScript
import { writeFile } from 'node:fs/promises'
import {
  defineSuiteCutAudioPlugin,
  encodePcm16Wav,
} from 'suitecut/audio-plugin'

export default defineSuiteCutAudioPlugin({
  async synthesize({ text, voice, speed, outputPath, options }) {
    const { samples, sampleRate } = await runModel({ text, voice, speed, options })
    await writeFile(outputPath, encodePcm16Wav(samples, sampleRate))
  },
})

Provider IDs use lowercase letters, numbers, dots, underscores, and hyphens. Plugins cannot replace kokoro or macos-say.