Packages
mob
0.6.24
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.11
0.5.10
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
BEAM-on-device mobile framework for Elixir
Current section
Files
Jump to
Current section
Files
lib/mob/speech.ex
defmodule Mob.Speech do
@moduledoc """
Text-to-speech. No permission required on either platform.
## Usage
def handle_event("read_aloud", _params, socket) do
Mob.Speech.speak(socket, socket.assigns.article_text)
{:noreply, socket}
end
Stop mid-utterance:
Mob.Speech.stop_speaking(socket)
## Options
| Option | Type | Meaning | Default |
|----------|---------|------------------------------------------|---------|
| `:rate` | float | Speech rate (0.0–1.0, platform-scaled) | system |
| `:pitch` | float | Pitch multiplier (0.5–2.0) | 1.0 |
| `:voice` | binary | BCP-47 language/voice id (e.g. `"en-US"`)| system |
Calling `speak/3` while speech is in progress enqueues the new utterance.
iOS uses `AVSpeechSynthesizer`; Android uses `TextToSpeech`.
"""
@opt_keys [:rate, :pitch, :voice]
@doc """
Speak `text` aloud. Fire-and-forget; returns the socket unchanged so it can be
used inline without disrupting a `handle_event`/`handle_info` return value.
Mob.Speech.speak(socket, "Returns a list.", rate: 0.5)
"""
@spec speak(Mob.Socket.t(), binary(), keyword()) :: Mob.Socket.t()
def speak(socket, text, opts \\ []) when is_binary(text) and is_list(opts) do
:mob_nif.tts_speak(text, :json.encode(speak_opts(opts)))
socket
end
@doc "Stop any in-progress speech immediately. Returns the socket."
@spec stop_speaking(Mob.Socket.t()) :: Mob.Socket.t()
def stop_speaking(socket) do
:mob_nif.tts_stop()
socket
end
@doc false
# Whitelist + stringify known options into a JSON-encodable map. Unknown keys
# are dropped so a typo can't reach the native layer as a surprise option.
# Public-but-undocumented so the encoding can be unit-tested without the NIF.
@spec speak_opts(keyword()) :: %{optional(String.t()) => term()}
def speak_opts(opts) do
opts
|> Keyword.take(@opt_keys)
|> Map.new(fn {k, v} -> {Atom.to_string(k), v} end)
end
end