OpenJTalk (open_jtalk_elixir v0.4.0)

View Source

Japanese text-to-speech for Elixir, powered by Open JTalk.

OpenJTalk.say("こんにちは")

Use say/2 to synthesize and play speech, or to_wav_binary/2 and to_wav_file/2 when the generated audio is needed directly.

{:ok, wav} = OpenJTalk.to_wav_binary("こんにちは", rate: 1.1)
{:ok, path} = OpenJTalk.to_wav_file("こんにちは", out: "/tmp/greeting.wav")

Options

Synthesis functions accept synth_option/0. say/2 also accepts player_option/0, and to_wav_file/2 additionally accepts :out.

Options are validated before work begins. Unknown keys, invalid playback modes, and non-positive timeouts raise ArgumentError; numeric synthesis values outside their supported ranges are clamped.

Runtime assets

Synthesis requires the open_jtalk executable, a dictionary containing sys.dic, and an HTS voice. Automatic lookup uses this order:

  1. OPENJTALK_CLI, OPENJTALK_DICTIONARY_DIR, or OPENJTALK_VOICE;
  2. the corresponding bundled asset under the application's priv/ directory;
  3. a supported system installation.

A per-call :dictionary or :voice option overrides automatic lookup for that request. Successful automatic resolutions are cached. After changing environment variables or moving assets at runtime, reset them before the next synthesis:

OpenJTalk.Assets.reset_cache()

Use info/0 to inspect each resolved path and whether it came from the environment, bundled assets, or the system.

Errors and diagnostics

Runtime failures return {:error, reason}. Common reasons include:

  • {:binary_missing, paths}, {:dictionary_missing, path}, or {:voice_missing, path} when a required component cannot be resolved;
  • {:open_jtalk_exit, status, output} when synthesis fails;
  • :no_player_found or {:player_failed, status, output} when playback fails.

A command status may be :timeout. If synthesis succeeds but playback does not, use to_wav_binary/2 to isolate the audio-player path and call info/0 to see which player was selected.

Summary

Types

Output gain in dB. Typical useful range is about -20..20 (values are clamped).

Entry describing a component path and where it came from.

Return type of info/0.

Pitch shift in semitones. Range: -24..24 (values are clamped).

Audio playback mode

Option accepted by playback functions.

Speaking rate multiplier. Range: 0.5..2.0 (values are clamped).

Options accepted by say/2 (synthesis + playback).

Option accepted by synthesis functions.

Voice color adjustment. Range: -0.8..0.8 (values are clamped).

A synthesis option, or :out with the destination WAV path.

Functions

Return the resolved executable, dictionary, voice, and audio player.

Play RIFF/WAV bytes already in memory.

Play a WAV from a file path. See play_wav_binary/2 for options.

Synthesize text with Open JTalk and play it.

Synthesize text and return RIFF/WAV bytes.

Synthesize text to a WAV file.

Validate options for synthesis and playback.

Types

gain()

@type gain() :: number()

Output gain in dB. Typical useful range is about -20..20 (values are clamped).

info_entry()

@type info_entry() :: %{
  path: String.t() | nil,
  source: :env | :bundled | :system | :none
}

Entry describing a component path and where it came from.

info_map()

@type info_map() :: %{
  bin: info_entry(),
  dictionary: info_entry(),
  voice: info_entry(),
  audio_player: info_entry()
}

Return type of info/0.

pitch_shift()

@type pitch_shift() :: -24..24

Pitch shift in semitones. Range: -24..24 (values are clamped).

playback_mode()

@type playback_mode() :: :auto | :file | :stdin

Audio playback mode:

  • :auto — prefer stdin when available; otherwise fall back to file playback
  • :file — always use file-based playback
  • :stdin — stream WAV bytes via stdin (diskless); falls back to file if unsupported

player_option()

@type player_option() :: {:timeout, pos_integer()} | {:playback_mode, playback_mode()}

Option accepted by playback functions.

  • :playback_mode - :auto (default), :stdin, or :file
  • :timeout - positive timeout in milliseconds; defaults to 20_000

rate()

@type rate() :: float()

Speaking rate multiplier. Range: 0.5..2.0 (values are clamped).

say_option()

@type say_option() :: player_option() | synth_option()

Options accepted by say/2 (synthesis + playback).

synth_option()

@type synth_option() ::
  {:timbre, timbre()}
  | {:pitch_shift, pitch_shift()}
  | {:rate, rate()}
  | {:gain, gain()}
  | {:voice, Path.t()}
  | {:dictionary, Path.t()}
  | {:timeout, pos_integer()}

Option accepted by synthesis functions.

  • :timbre - voice-color offset, clamped to -0.8..0.8; defaults to 0.0
  • :pitch_shift - semitone shift, clamped to -24..24; defaults to 0
  • :rate - speaking speed, clamped to 0.5..2.0; defaults to 1.0
  • :gain - output gain in dB, clamped to -20..20; defaults to 0
  • :voice - path to a .htsvoice file for this request
  • :dictionary - path to a directory containing sys.dic for this request
  • :timeout - positive timeout in milliseconds; defaults to 20_000

timbre()

@type timbre() :: float()

Voice color adjustment. Range: -0.8..0.8 (values are clamped).

wav_file_option()

@type wav_file_option() :: synth_option() | {:out, Path.t()}

A synthesis option, or :out with the destination WAV path.

Functions

info()

@spec info() :: {:ok, info_map()}

Return the resolved executable, dictionary, voice, and audio player.

Each entry includes its path and whether it came from an environment variable, bundled assets, the system, or no available source.

play_wav_binary(wav_bytes, opts \\ [])

@spec play_wav_binary(iodata(), [player_option()]) :: :ok | {:error, term()}

Play RIFF/WAV bytes already in memory.

:auto and :stdin stream to a stdin-capable player when possible and fall back to a temporary file when stdin playback is unavailable. :file always uses a temporary file.

play_wav_file(path, opts \\ [])

@spec play_wav_file(Path.t(), [player_option()]) :: :ok | {:error, term()}

Play a WAV from a file path. See play_wav_binary/2 for options.

say(text, opts \\ [])

@spec say(binary(), [say_option()]) :: :ok | {:error, term()}

Synthesize text with Open JTalk and play it.

The default :auto playback mode tries stdin first, then falls back to file playback. The generated WAV is not retained; use to_wav_file/2 followed by play_wav_file/2 when a persistent output file is required.

Example

:ok = OpenJTalk.say("こんにちは", pitch_shift: 2)

to_wav_binary(text, opts \\ [])

@spec to_wav_binary(binary(), [synth_option()]) :: {:ok, binary()} | {:error, term()}

Synthesize text and return RIFF/WAV bytes.

Example

{:ok, wav} = OpenJTalk.to_wav_binary("こんにちは", rate: 1.1)

to_wav_file(text, opts \\ [])

@spec to_wav_file(binary(), [wav_file_option()]) :: {:ok, Path.t()} | {:error, term()}

Synthesize text to a WAV file.

:out sets the destination path. Without it, a unique path is created in the system temporary directory.

Example

{:ok, path} = OpenJTalk.to_wav_file("こんにちは", out: "/tmp/greeting.wav")

validate_options!(opts)

@spec validate_options!(keyword()) :: keyword()

Validate options for synthesis and playback.

Allowed keys:

  • Synthesis: :timbre, :pitch_shift, :rate, :gain, :voice, :dictionary, :timeout
  • Playback: :playback_mode, :timeout
  • Files: :out

Enforcement:

  • Unknown keys raise ArgumentError
  • :playback_mode must be one of :auto | :file | :stdin (if present)

  • :timeout must be a positive integer (if present)

Returns the original opts on success.