talkie

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2026 License: Apache-2.0 Imports: 27 Imported by: 0

README

talkie-core

A voice front end for agents, in Go. talkie listens for a wake word, hears what the user says, hands it to your agent, and speaks the agent's reply as it streams in. The user can interrupt the reply. Everything runs in-process on ONNX Runtime:

Stage Model Library
Wake word PhonMatchNet (any phrase, no training data) go-phonmatchnet
Voice activity Silero VAD v5 go-silero-vad
Speech to text Parakeet TDT 0.6B v3 go-parakeet
Text to speech Kokoro-82M v1.0 go-kokoro
Echo cancellation SpeexDSP 1.2.1, ported to Go go-speexdsp

Audio devices go through malgo (miniaudio).

Requirements

  • Go 1.25 and a C compiler: talkie uses CGO for ONNX Runtime and audio.
  • Linux on x86-64, with a microphone and speaker. It has been tested on a desktop with PipeWire. miniaudio also supports macOS and Windows, but talkie hasn't been tried there.
  • ONNX Runtime 1.22, and the Parakeet and Kokoro model files (about 1 GB). scripts/get-assets.sh downloads them; see below.
  • Optional: an NVIDIA GPU with CUDA 12 and cuDNN 9, to run speech-to-text and text-to-speech on the GPU.

Getting started

  1. Download ONNX Runtime and the models into ./assets. Every file is pinned and checked against its SHA256, and running it again resumes or skips what is already there:

    scripts/get-assets.sh            # CPU, Parakeet INT8: about 1 GB
    scripts/get-assets.sh --cuda     # GPU build of ONNX Runtime
    scripts/get-assets.sh --cuda --fp32   # plus Parakeet FP32: about 3 GB
    

    It prints the exact commands for the next two steps. Silero VAD and the wake word model are embedded in their Go libraries, so there is nothing else to download. Where the files come from:

    On another platform, such as a Jetson, install ONNX Runtime yourself and use --no-ort.

  2. Train talkie to your room, once per microphone and speaker. This takes about a minute: stay quiet while it measures the room, then say the wake word ("hey computer") five times and read a sentence aloud.

    go run ./cmd/talkie-demo -train -ort assets/onnxruntime/lib/libonnxruntime.so -kokoro assets/kokoro
    
  3. Talk to it:

    go run ./cmd/talkie-demo -ort assets/onnxruntime/lib/libonnxruntime.so \
        -parakeet assets/parakeet -int8 -kokoro assets/kokoro
    

    A short chime means it's listening.

    • Say "hey computer, what time is it?" and it repeats what it heard.
    • "Hey computer, tell me a story" gives a long reply. Interrupt it with "hey computer", or by pressing Enter.

    Add -cuda if you downloaded the GPU build. Leave out -int8 if you downloaded --fp32.

Quick start

agent := talkie.AgentFunc(func(ctx context.Context, turn talkie.Turn, reply *talkie.Reply) error {
    // Write the answer as it is generated; talkie speaks it a sentence at a time.
    _, err := reply.WriteString("You said: " + turn.Text)
    return err // ctx is cancelled if the user interrupts
})
t, err := talkie.New(ctx, talkie.Config{
    ORTLibrary: "/opt/onnxruntime/lib/libonnxruntime.so",
    CUDA:       true,
    Parakeet:   talkie.ParakeetConfig{Dir: "/models/parakeet"},
    Kokoro: talkie.KokoroConfig{
        ModelPath:  "/models/kokoro/kokoro-v1.0.onnx",
        VoicesPath: "/models/kokoro/voices-v1.0.bin",
    },
    WakeWord: "hey computer", // also interrupts the reply
}, agent)
if err != nil {
    return err
}
defer t.Close()
return t.Run(ctx) // talks through the default microphone and speaker until ctx is done

The agent

talkie calls Agent.Respond once for each thing the user says. It runs on its own goroutine.

  • Turn carries:

    • Text, with the wake phrase removed;
    • Session, and Reply, an ID matching the reply events;
    • Trigger: the wake word, a follow-up, push-to-talk, a barge-in, or a continuation.
  • Reply is an io.Writer. Text is spoken a sentence at a time as it arrives, so stream your LLM's tokens straight into it.

    • Reply.Then chooses what happens after the reply: listen for a follow-up (the default), end the session, or wait for the agent to speak again.
  • Interruption: when the user interrupts, ctx is cancelled and writes fail. The next Turn.Interrupted says how much of the reply the user heard:

    • Heard: the sentences that played completely;
    • Cut: the sentence playing when the user interrupted;
    • Unspoken: what was written but never played.

    Agents that keep a history should replace their reply with what was heard.

  • Errors: a returned error is reported as an EventError, and talkie goes back to listening.

Barge-in

Config.BargeIn chooses what interrupts the agent by voice:

  • BargeInWakeWord (the zero value): only the wake word. The agent's own voice can't interrupt it. Without a wake word, nothing interrupts by voice.
  • BargeInVoice: the user can also talk over it.
    • A suspected interruption briefly ducks the reply and checks that the voice stays louder than the reply's predicted echo.
    • The echo canceller removes most of the reply from the microphone.
    • A loud speaker can still make the agent interrupt itself. Room training says when to turn it down.
  • BargeInOff: replies play to the end.

Talkie.Interrupt stops the agent in any mode, for a key or a button. When nothing is playing, it starts listening as push-to-talk does (Talkie.Listen). A wake word the agent itself just said is ignored.

Cues

For a device without a screen, Config.Cues plays short sounds that say what talkie is doing. Each is optional, and the zero value plays none:

  • Listening: talkie started listening at the user's request: the wake word, Talkie.Listen opening a session, or Talkie.Interrupt. Follow-up windows after a reply are silent.
  • Thinking, looped: from half a second after the user stops talking (ThinkingDelay), through transcription and the agent's work, until the reply starts, the user interrupts, or the session ends.
  • Idle: the session ended, and talkie waits for the wake word.

DefaultCues has built-in sounds: two glassy notes a fifth up for listening, the same down for idle, and a quiet fifth that swells and fades every 1.6 s while it thinks. ReadWAV reads your own, in any rate, mono or not. talkie plays cues through its own output, so the echo canceller removes them from the microphone like the agent's voice. They don't count as the agent speaking.

Events

Config.OnEvent receives what happens as it happens:

  • state changes (idle, listening, hearing, transcribing, thinking, speaking);
  • the wake word, listening windows, and speech started or discarded;
  • transcripts, with the transcription time;
  • replies started, finished and stopped, with how far they played;
  • interruptions and what caused them;
  • latency measurements, the room profile no longer fitting, and errors.

It is called on the goroutine that runs the audio: return quickly. Config.Logger logs the same events, plus the barge-in tuning details at Debug.

Room training

Train measures the room through the devices, and SaveProfile saves it under ~/.config/talkie/rooms/, one profile per microphone and speaker pair. It measures:

  • the background noise;
  • the speaker-to-microphone delay and echo level, from quiet chirps. It advises turning the volume down when the echo is too loud for barge-in;
  • the echo canceller's learned filter, while Kokoro speaks a paragraph;
  • the wake word's threshold, tuned to the user's voice: they say it five times, then read a sentence.

Run loads the profile for its devices automatically. Config.IgnoreRoomProfile opts out, for a device that moves between rooms.

Run starts with a short chime, from whose echo it measures the session's latency (device opens vary); Config.DisableReadyChime turns it off. After each reply, talkie measures the echo path again. An EventRoomChanged means the profile no longer fits and training should be repeated.

Demo

cmd/talkie-demo is a toy agent that repeats what it heard and tells a story to interrupt:

go run ./cmd/talkie-demo -train -ort $ORT -kokoro assets/kokoro   # once per room
go run ./cmd/talkie-demo -cuda -ort $ORT -parakeet assets/parakeet -kokoro assets/kokoro
  • Useful flags:
    • -list lists the devices; -input and -output pick them, by part of their names;
    • -cues plays the built-in cues;
    • -barge-in wake|voice|off (default wake);
    • -key reads a keyboard's event device so the Pause key interrupts;
    • -v logs every state change.
  • Enter interrupts, or talks without the wake word.

Tests

  • go test ./... runs everything that needs no native library.
  • TALKIE_TEST_ORT=/path/libonnxruntime.so adds the conversation scenarios: real Silero VAD, a simulated room with echo, and recorded speech.
  • TALKIE_TEST_PARAKEET (the directory, FP32 or INT8) and TALKIE_TEST_KOKORO (the directory with both files) add whole conversations through the real models. Add TALKIE_TEST_CUDA=1 to use the GPU.

Known limits

  • Linux on x86-64 with PipeWire is the only tested platform. macOS, Windows and arm64 (such as a Jetson) need their own testing.
  • English only: Kokoro speaks with its US and UK English voices, and PhonMatchNet's wake words are English.
  • The same devices' round trip can differ by about 60 ms between opens, and it drifts over hours. The ready chime measures each session, but a restored echo canceller is left out when this session's latency is more than 25 ms from the trained range, and the canceller then learns the room from scratch.
  • With BargeInVoice, the reply can dip in volume for a moment near its start: before the echo canceller has settled, the reply's echo can look like the user talking over it, and checking that ducks the reply.
  • A Kokoro model run in progress can't be interrupted, so barge-in waits up to one run (KokoroConfig.MaxChunkPhonemes).
  • The wake word and voice activity always run on the CPU.
  • Room profiles aren't migrated: one of another format version, or whose echo canceller snapshot doesn't restore, needs training again.
  • One Run at a time per Talkie.

License

Apache-2.0; see LICENSE, NOTICE and THIRD_PARTY.md. The models have their own licenses; see each library's ASSETS.md.

Documentation

Overview

Package talkie is a voice front end for agents: wake word, voice activity detection, speech-to-text and text-to-speech with barge-in, on ONNX Runtime models (go-phonmatchnet, go-silero-vad, go-parakeet and go-kokoro) running in-process.

New loads the models; Run talks with the user through the audio devices, handing what they say to an Agent and speaking its Reply as it is written. Train measures a room once so Run can start tuned to it, and Config.OnEvent reports what the conversation is doing.

Example

The README's quick start: an agent that repeats what it heard.

package main

import (
	"context"
	"log"

	talkie "github.com/androiddrew/talkie-core"
)

func main() {
	ctx := context.Background()
	agent := talkie.AgentFunc(func(ctx context.Context, turn talkie.Turn, reply *talkie.Reply) error {
		// Write the answer as it is generated; talkie speaks it a sentence at a time.
		_, err := reply.WriteString("You said: " + turn.Text)
		return err // ctx is cancelled if the user interrupts
	})
	t, err := talkie.New(ctx, talkie.Config{
		ORTLibrary: "/opt/onnxruntime/lib/libonnxruntime.so",
		CUDA:       true,
		Parakeet:   talkie.ParakeetConfig{Dir: "/models/parakeet"},
		Kokoro: talkie.KokoroConfig{
			ModelPath:  "/models/kokoro/kokoro-v1.0.onnx",
			VoicesPath: "/models/kokoro/voices-v1.0.bin",
		},
		WakeWord: "hey computer", // also interrupts the reply
		OnEvent: func(ev talkie.Event) {
			if ev.Type == talkie.EventState {
				log.Println(ev.State) // drive a status light
			}
		},
	}, agent)
	if err != nil {
		log.Fatal(err)
	}
	defer t.Close()
	if err := t.Run(ctx); err != nil { // the default microphone and speaker, until ctx is done
		log.Fatal(err)
	}
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNoProfile = errors.New("talkie: no room profile")

ErrNoProfile is returned by LoadProfile when there is no profile file.

Functions

func DefaultProfileDir

func DefaultProfileDir() (string, error)

DefaultProfileDir is where Run looks for profiles unless Config.RoomProfileDir says otherwise: talkie/rooms in the user's configuration directory, such as ~/.config/talkie/rooms.

func ProfilePath

func ProfilePath(dir, input, output string) string

ProfilePath is the file in dir for a microphone and speaker: a readable slug of their names plus a short hash, so similar names don't collide.

func SaveProfile

func SaveProfile(path string, p Profile) error

SaveProfile writes p to path, creating its directory. It replaces any file there in one step, so a reader never sees half a profile.

Types

type Agent

type Agent interface {
	Respond(ctx context.Context, turn Turn, reply *Reply) error
}

Agent answers what the user says. talkie calls Respond once per transcript, on its own goroutine, and speaks what the agent writes to the reply as it is written.

ctx is cancelled when the user interrupts the reply (barge-in) or the session ends: stop generating and return. The next Turn's Interrupted then says how much of this reply the user heard.

type AgentFunc

type AgentFunc func(ctx context.Context, turn Turn, reply *Reply) error

AgentFunc adapts a function to the Agent interface.

func (AgentFunc) Respond

func (f AgentFunc) Respond(ctx context.Context, turn Turn, reply *Reply) error

Respond calls f.

type BargeIn

type BargeIn int

BargeIn is how the user can interrupt the agent by voice, while it talks or thinks.

const (
	// BargeInWakeWord, the default, interrupts only on the wake word: the
	// agent's own echo can't interrupt it, and the user needs the wake word
	// to. Without Config.WakeWord, nothing interrupts by voice.
	BargeInWakeWord BargeIn = iota
	// BargeInVoice also interrupts when the user talks over the agent. A
	// loud speaker can make the agent interrupt itself; room training says
	// when to turn it down.
	BargeInVoice
	// BargeInOff never interrupts by voice: replies play to the end unless
	// the application calls Talkie.Interrupt, as from a key or a button.
	BargeInOff
)

type CancellerProfile

type CancellerProfile struct {
	// Snapshot is the canceller's state (go-speexdsp's echo.Canceller
	// Snapshot); it restores only into talkie's canceller configuration.
	Snapshot []byte
	// ERLEDB is the echo it removed at the end of training, in dB.
	ERLEDB float64
}

CancellerProfile is a trained echo canceller. Run restores it so the first reply starts with the echo path already learned.

type Config

type Config struct {
	// ORTLibrary is the ONNX Runtime library, a path or OS-loader name,
	// shared by every model. A CUDA build is needed for CUDA.
	ORTLibrary string
	// CUDA runs speech-to-text and text-to-speech on GPU DeviceID. The wake
	// word and VAD always run on the CPU.
	CUDA     bool
	DeviceID int

	Parakeet ParakeetConfig
	Kokoro   KokoroConfig

	// WakeWord is the phrase that starts a session, such as "hey computer".
	// Empty disables it: sessions start with Listen or when the agent speaks.
	WakeWord string
	// WakeFAR selects PhonMatchNet's operating point by its target
	// false-accept rate; zero selects 0.01.
	WakeFAR float64

	// OutputRate is the speaker's sample rate; zero selects 48 kHz.
	OutputRate int

	// InputDevice and OutputDevice select the microphone and speaker by
	// name, ignoring case: a device named exactly that, or else the first
	// whose name contains it. PulseAudio's and PipeWire's monitor sources
	// ("Monitor of ...") are skipped unless InputDevice says "monitor", so a
	// USB headset's name selects its microphone. Empty selects the system
	// defaults. OutputRate is the rate both run at.
	InputDevice, OutputDevice string

	// AudioLatency is the audio devices' round trip, from writing a sample
	// to the speaker to hearing it at the microphone. Barge-in waits it out
	// before judging a voice against the agent's echo, and the follow-up
	// window ignores the agent's last words for that long. Zero assumes
	// 100 ms; PipeWire and Bluetooth devices are often slower.
	AudioLatency time.Duration

	// RoomProfileDir is where Run looks for the room profile of its
	// devices; empty selects DefaultProfileDir. IgnoreRoomProfile makes Run
	// use only this Config.
	RoomProfileDir    string
	IgnoreRoomProfile bool

	// Logger, if set, receives the conversation's events at Info level and
	// their details at Debug.
	Logger *slog.Logger

	// OnEvent, if set, receives the conversation's events as they happen:
	// state changes, the wake word, transcripts, replies, interruptions,
	// latency measurements and errors. It is called on Run's goroutine,
	// which also runs the audio: return quickly, and hand anything slow to
	// another goroutine.
	OnEvent func(Event)

	// DisableEchoCancellation turns off the built-in echo canceller
	// (go-speexdsp), for a headset or a microphone that is already
	// echo-cancelled, such as PipeWire's echo-cancel source. Barge-in still
	// verifies interruptions against the predicted echo.
	DisableEchoCancellation bool

	// BargeIn sets how the user can interrupt the agent by voice; the zero
	// value, BargeInWakeWord, allows only the wake word, so the agent's own
	// echo can't interrupt it. Talkie.Interrupt works whatever it is set to.
	BargeIn BargeIn

	// Cues are sounds that say when talkie starts listening, while it
	// thinks, and when the session ends, for a device without a screen; the
	// zero value plays none. DefaultCues has built-in ones.
	Cues Cues

	// DisableReadyChime turns off the short chime Run plays when it starts.
	// Run measures each session's audio latency from the chime's echo,
	// since it can differ between device opens; without it, the latency
	// comes from the room profile until the first reply measures it.
	DisableReadyChime bool
}

Config selects the models. Every model runs in-process on one ONNX Runtime environment.

type Cues added in v0.2.0

type Cues struct {
	// Listening plays when talkie starts listening at the user's request:
	// the wake word, Talkie.Listen opening a session, or Talkie.Interrupt.
	// Follow-up windows after a reply are silent.
	Listening *Sound
	// Thinking loops while the user waits: from ThinkingDelay after they
	// stop talking, through transcription and the agent's work, until the
	// reply starts playing, the user interrupts, or the session ends. It
	// should loop seamlessly. ThinkingDelay is zero for 500 ms, so a quick
	// reply doesn't blip.
	Thinking      *Sound
	ThinkingDelay time.Duration
	// Idle plays when the session ends and talkie waits for the wake word.
	Idle *Sound
}

Cues are short sounds that say what talkie is doing, for a device without a screen. Each is optional: a nil Sound plays nothing. talkie plays them through its own output, so the echo canceller removes them from the microphone like the agent's voice; they don't count as the agent speaking.

func DefaultCues added in v0.2.0

func DefaultCues() Cues

DefaultCues returns talkie's built-in cues, at 48 kHz: two glassy notes a fifth up for Listening, the same down and a little quieter for Idle, and for Thinking a quiet fifth that swells and fades every 1.6 s.

type EchoProfile

type EchoProfile struct {
	// Heard is false when the microphone didn't hear the speaker, as with a
	// headset; echo cancellation can then be turned off.
	Heard bool
	// GainDB is the echo's level relative to what the speaker was given,
	// direct path and reverb. At or below -3 dB barge-in works; above 0 dB
	// the echo is louder than the voice that made it, and the user can't be
	// heard over it.
	GainDB float64
	// Clipped reports that the microphone reached full scale during a quiet
	// chirp: its gain is far too high.
	Clipped bool
	// Advice says what to change in the room, or is empty.
	Advice string
}

EchoProfile is how loud the speaker's sound is when it reaches the microphone.

type Event

type Event struct {
	Type EventType
	// Time is when it happened; At is the same moment in the audio stream,
	// since Run started.
	Time time.Time
	At   time.Duration
	// Session is the session it belongs to, empty outside one.
	Session string

	// State and From are the new and previous state, for EventState.
	State, From State
	// Trigger is what opened the session, listening window, utterance or
	// transcript, for EventSessionStarted, EventListening,
	// EventSpeechStarted and EventTranscript.
	Trigger Trigger
	// Reason says why, for EventSessionEnded, EventSpeechDiscarded and
	// EventReplyStopped.
	Reason string
	// Timeout is how long the listening window stays open, for
	// EventListening.
	Timeout time.Duration

	// Text is what the user said, without the wake phrase, for
	// EventTranscript; STT is how long transcribing it took, and Speech how
	// long the user spoke.
	Text        string
	STT, Speech time.Duration

	// Reply identifies the agent's reply, for the EventReply* events and
	// EventInterrupted; Heard, Cut, Unspoken and Played are how far it
	// played, as in Interruption.
	Reply                string
	Heard, Cut, Unspoken string
	Played               time.Duration
	// InterruptedBy and While are what interrupted the agent and what it
	// was doing, StateSpeaking or StateThinking, for EventInterrupted.
	InterruptedBy InterruptedBy
	While         State

	// Latency is the speaker-to-microphone round trip, for EventLatency
	// and EventRoomChanged; LatencySource says how it was measured, and
	// EchoGainDB is the echo's level.
	Latency       time.Duration
	LatencySource LatencySource
	EchoGainDB    float64

	// Err is what failed, for EventError.
	Err error
}

Event is something that happened in the conversation, for an application to show or measure: Config.OnEvent receives them. Type says which fields are set.

type EventType

type EventType string

EventType names an event.

const (
	EventSessionStarted  EventType = "session_started"  // Trigger
	EventSessionEnded    EventType = "session_ended"    // Reason: "timeout", "agent_timeout", "client", "agent"
	EventState           EventType = "state"            // State, From
	EventWake            EventType = "wake"             // the wake word was heard
	EventListening       EventType = "listening"        // Trigger, Timeout
	EventSpeechStarted   EventType = "speech_started"   // Trigger
	EventSpeechDiscarded EventType = "speech_discarded" // Reason: "too_short", "no_text", "stt_error", "session_ended"
	EventTranscript      EventType = "transcript"       // Text, Trigger, STT, Speech
	EventReplyStarted    EventType = "reply_started"    // Reply
	EventReplyFinished   EventType = "reply_finished"   // Reply, Heard, Played
	EventReplyStopped    EventType = "reply_stopped"    // Reply, Reason, Heard, Cut, Unspoken, Played
	EventInterrupted     EventType = "interrupted"      // InterruptedBy, While, and the reply's progress
	EventLatency         EventType = "latency"          // Latency, LatencySource, EchoGainDB
	EventRoomChanged     EventType = "room_changed"     // Latency: the room profile no longer fits
	EventError           EventType = "error"            // Err
)

type InterruptedBy

type InterruptedBy string

InterruptedBy is what interrupted the agent.

const (
	InterruptedBySpeech InterruptedBy = "speech"    // the user talked over it
	InterruptedByWake   InterruptedBy = "wake_word" // the user said the wake word
	InterruptedByApp    InterruptedBy = "app"       // Talkie.Interrupt, as from a key
)

type Interruption

type Interruption struct {
	Heard    string // sentences that played completely
	Cut      string // the sentence playing when the user interrupted
	Unspoken string // what the agent wrote that never played
	// Played is how long the reply played; zero when the user spoke while
	// the agent was still thinking.
	Played time.Duration
}

Interruption is how much of a reply the user heard before cutting it off. The three texts are the agent's own words, split at sentence boundaries.

type KokoroConfig

type KokoroConfig struct {
	ModelPath  string // kokoro-v1.0.onnx
	VoicesPath string // voices-v1.0.bin
	// Voice is a US (a*) or UK (b*) English voice; empty selects af_heart.
	Voice string
	// Speed is 0.5 to 2; zero selects 1.
	Speed float32
	// FirstChunkPhonemes and MaxChunkPhonemes limit each model run. A run in
	// progress can't be interrupted, so shorter runs make barge-in quicker
	// and start the first sentence sooner. Zero selects 80 and 160.
	FirstChunkPhonemes, MaxChunkPhonemes int
}

KokoroConfig locates the Kokoro v1.0 files and sets the voice.

type LatencyProfile

type LatencyProfile struct {
	Runs             []time.Duration
	Min, Median, Max time.Duration
}

LatencyProfile is the round trip measured on several opens of the devices, which can differ by tens of milliseconds between opens.

func (LatencyProfile) Assumed

func (l LatencyProfile) Assumed() time.Duration

Assumed is the latency Run uses: the middle of the measured range, which keeps barge-in working across it. Timing barge-in for the longest or the shortest run fails at the other end of the range.

type LatencySource

type LatencySource string

LatencySource is how a latency was measured.

const (
	LatencyFromChime LatencySource = "chime" // the ready chime, before the first reply
	LatencyFromReply LatencySource = "reply" // the echo of a reply
)

type ParakeetConfig

type ParakeetConfig struct {
	// Dir holds nemo128.onnx, encoder-model.onnx (with its .onnx.data) and
	// decoder_joint-model.onnx.
	Dir string
	// INT8 uses encoder-model.int8.onnx and decoder_joint-model.int8.onnx,
	// smaller and faster on CPU.
	INT8 bool
}

ParakeetConfig locates the Parakeet TDT 0.6B v3 ONNX files.

type Profile

type Profile struct {
	Trained time.Time
	// Input and Output are the full names of the microphone and speaker, as
	// the system reports them; Rate is the rate they were trained at.
	Input, Output string
	Rate          int

	// Latency is the speaker-to-microphone round trip.
	Latency LatencyProfile
	// Echo is how loud the speaker is at the microphone.
	Echo EchoProfile
	// NoiseDBFS is the room's background level at the microphone, in dB
	// relative to full scale.
	NoiseDBFS float64
	// Canceller is the echo canceller as it learned the room, or nil when
	// training didn't play speech.
	Canceller *CancellerProfile
	// Wake is the wake word tuned to the user, or nil when training had no
	// wake word.
	Wake *WakeProfile
}

Profile is what room training learned about one microphone and speaker. Run loads the profile for its devices and tunes itself with it. Train creates one; SaveProfile and LoadProfile store it.

func LoadProfile

func LoadProfile(path string) (Profile, error)

LoadProfile reads the profile at path.

func Train

func Train(ctx context.Context, cfg Config, prompt Prompt) (Profile, error)

Train measures the room through cfg's devices: the background noise, then the speaker-to-microphone delay and level from quiet chirps. When cfg.Kokoro names the Kokoro files (with cfg.ORTLibrary), it then speaks a short paragraph while the echo canceller learns the room, so Run can start with it learned. When cfg.WakeWord is set (with cfg.ORTLibrary), the user then says the wake word a few times and reads a sentence, to tune the wake word's threshold to their voice. Save the result with SaveProfile at ProfilePath(dir, p.Input, p.Output) for Run to find it.

type Prompt

type Prompt func(ctx context.Context, step Step) error

Prompt shows a step's instruction and returns when the user is ready, or with an error to stop training. A terminal application prints and waits for Enter; a graphical one shows a dialog.

type Reply

type Reply struct {
	// contains filtered or unexported fields
}

Reply is where an agent writes its answer. Text is spoken as it arrives, a sentence at a time. Write returns an error once the reply is interrupted.

func (*Reply) Then

func (r *Reply) Then(then Then)

Then sets what the conversation does after the reply has played; ThenListen by default.

func (*Reply) Write

func (r *Reply) Write(p []byte) (int, error)

Write adds text to the reply.

func (*Reply) WriteString

func (r *Reply) WriteString(s string) (int, error)

WriteString adds text to the reply.

type Sound added in v0.2.0

type Sound struct {
	Samples []float32
	Rate    int
}

Sound is mono audio for a cue: Samples in [-1, 1] at Rate samples per second. talkie resamples it to Config.OutputRate.

func ReadWAV added in v0.2.0

func ReadWAV(r io.Reader) (*Sound, error)

ReadWAV reads a WAV file's audio as a Sound for a cue: 8-, 16-, 24- or 32-bit integer PCM or 32-bit float, with any number of channels, which are averaged to mono.

type State

type State string

State is where the conversation is.

const (
	StateIdle         State = "idle"         // no session: only the wake word is listened for
	StateListening    State = "listening"    // waiting for the user to talk
	StateHearing      State = "hearing"      // the user is talking
	StateTranscribing State = "transcribing" // the user stopped; transcribing
	StateThinking     State = "thinking"     // the agent has the transcript and hasn't spoken yet
	StateSpeaking     State = "speaking"     // the agent's reply is playing
)

type Step

type Step struct {
	Name        string // "noise", "echo", "canceller", "wake", "speech"
	Instruction string // what to tell the user
}

Step is one stage of room training, for the application to present.

type Talkie

type Talkie struct {
	// contains filtered or unexported fields
}

Talkie is a loaded voice front end for an agent.

func New

func New(ctx context.Context, cfg Config, agent Agent) (_ *Talkie, err error)

New loads the models and returns a front end that hands what the user says to agent. Close is mandatory.

func (*Talkie) Close

func (t *Talkie) Close() error

Close releases the models. The ONNX Runtime environment stays loaded until the process exits.

func (*Talkie) EndSession

func (t *Talkie) EndSession()

EndSession ends the conversation in progress, if any.

func (*Talkie) Interrupt

func (t *Talkie) Interrupt()

Interrupt stops the agent, whether it is talking or thinking, and listens to the user without the wake word, whatever Config.BargeIn is set to: for a key or a button. When the agent isn't talking or thinking it listens, as Listen does. It takes effect on Run's goroutine.

func (*Talkie) Listen

func (t *Talkie) Listen()

Listen opens a listening window as push-to-talk does: the user can speak without the wake word. It takes effect on Run's goroutine.

func (*Talkie) Run

func (t *Talkie) Run(ctx context.Context) error

Run talks with the user through the audio devices until ctx is done. It opens Config.InputDevice and Config.OutputDevice, or the system defaults, at Config.OutputRate. Unless Config.DisableReadyChime, it starts with a short chime, from whose echo it measures this session's audio latency.

type Then

type Then string

Then is what the conversation does after a reply has played.

const (
	// ThenListen opens a follow-up window: the user can answer without the
	// wake word. It is the default.
	ThenListen Then = "listen"
	// ThenEnd ends the session; the user needs the wake word again.
	ThenEnd Then = "end"
	// ThenWait waits for the agent to speak again, for example after "let
	// me check".
	ThenWait Then = "wait"
)

type Trigger

type Trigger string

Trigger is what opened an utterance.

const (
	TriggerWake         Trigger = "wake_word"    // the user said the wake word
	TriggerFollowup     Trigger = "followup"     // the user answered after a reply
	TriggerCommand      Trigger = "command"      // push-to-talk
	TriggerBargeIn      Trigger = "barge_in"     // the user interrupted the agent
	TriggerContinuation Trigger = "continuation" // the user kept talking after a pause
)

type Turn

type Turn struct {
	Session string  // the conversation it belongs to
	Reply   string  // the reply's ID, as in Event.Reply
	Text    string  // what the user said, with any leading wake phrase removed
	Trigger Trigger // what opened the utterance
	// Interrupted is set when the user cut off the previous reply to say
	// this. Agents that keep a history should replace the reply they wrote
	// with what was heard.
	Interrupted *Interruption
}

Turn is one thing the user said.

type WakeProfile

type WakeProfile struct {
	// Keyword is the wake word it was trained for; Run uses Threshold only
	// for the same wake word.
	Keyword string
	// Threshold is the PhonMatchNet score at or above which the wake word
	// is heard, between the user saying it and the user's ordinary speech;
	// zero when training couldn't separate them, and PhonMatchNet's default
	// operating point is kept.
	Threshold float64
	// Positives are the scores of the times the user said the wake word;
	// NegativeMax is the highest score anywhere in their ordinary speech.
	// The threshold sits below all but one in five of the positives.
	Positives   []float64
	NegativeMax float64
	// Advice says what to change, or is empty.
	Advice string
}

WakeProfile is the wake word tuned to the user's voice in the room.

Directories

Path Synopsis
cmd
talkie-demo command
Command talkie-demo talks with you through your microphone and speaker using talkie's models and a toy agent that repeats what it heard.
Command talkie-demo talks with you through your microphone and speaker using talkie's models and a toy agent that repeats what it heard.
internal
audio
Package audio holds talkie's audio plumbing: a ring buffer addressed by absolute sample position and the player that owns the speaker.
Package audio holds talkie's audio plumbing: a ring buffer addressed by absolute sample position and the player that owns the speaker.
calibrate
Package calibrate measures a room from what talkie plays and what the microphone hears: the speaker-to-microphone delay and level from a chirp, and the noise floor from silence.
Package calibrate measures a room from what talkie plays and what the microphone hears: the speaker-to-microphone delay and level from a chirp, and the noise floor from silence.
device
Package device opens a full-duplex audio device with miniaudio (malgo): one callback per period receives the microphone samples and fills the speaker samples, so what was played and what was heard stay aligned for echo cancellation.
Package device opens a full-duplex audio device with miniaudio (malgo): one callback per period receives the microphone samples and fills the speaker samples, so what was played and what was heard stay aligned for echo cancellation.
engine
Package engine is talkie's conversation state machine, ported from wonky-talkie's engine.
Package engine is talkie's conversation state machine, ported from wonky-talkie's engine.
sim
Package sim is talkie's test bench: real speech clips, the real Silero VAD, a scripted wake word and a simulated room whose speaker echoes into the microphone.
Package sim is talkie's test bench: real speech clips, the real Silero VAD, a scripted wake word and a simulated room whose speaker echoes into the microphone.
wake
Package wake slides a fixed window over 16 kHz audio, scores it every hop and debounces accepted windows into wake-word detections.
Package wake slides a fixed window over 16 kHz audio, scores it every hop and debounces accepted windows into wake-word detections.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL