====== cterm_lib.js ======

The CTerm JavaScript library ([[https://gitlab.synchro.net/main/sbbs/-/blob/master/exec/load/cterm_lib.js?ref_type=heads|cterm_lib.js]]) exposes the //enhanced// features of [[http://syncterm.net|SyncTERM]] and other CTerm-compatible terminals to Synchronet JavaScript: terminal capability detection, loadable fonts, palette redefinition, detection of high-resolution graphics (Sixel, JPEG-XL) support, screen and pixel geometry queries, and digital audio playback.

Most of these features exist only on a capable terminal (principally SyncTERM), so the library also provides ''supports_*()'' functions to detect a feature before using it. On a terminal that lacks the feature, the corresponding output is simply ignored by the terminal, so a capability check is about avoiding wasted work and graceful fallback rather than preventing errors.

===== Loading =====

''cterm_lib.js'' is a [[:custom:javascript:lib:|Return-style]] library: load it into an empty scope object and keep the returned object.

<code javascript>
var cterm = load({}, 'cterm_lib.js');
if (cterm.supports_jpegxl())
    console.write(my_jpegxl_image);
</code>

When first loaded, the library queries the terminal's device attributes to learn its CTerm version and caches the result in the ''console'' object (so it is shared among multiple JavaScript contexts). Load it from a script running on a user's terminal session, not from an unconnected context such as a timed event.

===== Capability detection =====

Each function reports whether the connected terminal supports a feature. All return a boolean except ''supports_fonts()'', which may also return ''undefined'' for an older CTerm (e.g. SyncTERM 1.0) that predates feature queries.

^ Function ^ True when the terminal supports... ^
| ''supports_fonts()''       | loadable / selectable fonts |
| ''supports_palettes()''    | palette (color) redefinition |
| ''supports_sixel()''       | [[https://en.wikipedia.org/wiki/Sixel|Sixel]] bitmap graphics |
| ''supports_jpegxl()''      | JPEG-XL image display |
| ''supports_audio()''       | the audio channel (at least synthesized tones) |
| ''supports_audio_files()'' | audio //and// decoding of sound files (client %%libsndfile%%) |

<code javascript>
var cterm = load({}, 'cterm_lib.js');
if (cterm.supports_sixel())
    console.write(my_sixel_image);
</code>

===== Fonts =====

For the common case of loading the fonts configured in ''[[config:fonts.ini]]'', use the higher-level [[:custom:javascript:lib:fonts.js|fonts.js]] library instead. ''cterm_lib.js'' provides the lower-level primitives it is built on:

  * ''load_font(slot, data, force)'' -- load raw font bitmap ''data'' into terminal font ''slot'' (''43''..''255'').
  * ''activate_font(style, slot, wantblink)'' -- select a loaded ''slot'' for a display ''style'' (''font_styles.normal'', ''.high'', ''.blink'', ''.highblink'').
  * ''activate_fonts(slots)'' -- activate an array of ''{ style, slot }'' selections at once.

===== Palettes =====

  * ''redefine_palette(palette, bits)'' -- redefine the terminal's color palette. ''palette'' is an array of RGB entries; ''bits'' is the per-channel bit depth of the supplied values.
  * ''reset_palette()'' -- restore the terminal's default palette.
  * ''bright_background(enable)'' -- enable (or disable) bright background colors (high-intensity background attribute rather than blink).

===== Screen geometry =====

  * ''cterm_screen_geometry()'' -- resolve the terminal's character //and// pixel geometry for pixel-addressed protocols (image tiers, [[https://en.wikipedia.org/wiki/Z-machine|Z-machine]] v6, etc.). Returns ''{ cols, rows, cellW, cellH, pxW, pxH }'', falling back to sane defaults (an 8-pixel-wide graphics cell) when the terminal does not report a value.
  * ''query_graphicsdim()'' -- the terminal's graphics canvas size, in pixels.

===== Audio =====

On SyncTERM v1.10 and later, ''cterm_lib.js'' can play digital **sound effects** and **music** through the terminal's audio channel. The terminal keeps 256 //patch slots// of decoded audio and mixes them on channels 2..15; the library manages those slots and channels for you.

Check capability first: ''supports_audio_files()'' is true when the terminal can decode a sound **file**, and ''supports_audio()'' is true when at least synthesized **tones** are available (a terminal built without %%libsndfile%%). On any other terminal the audio calls are harmless no-ops.

Sound **data** is a file's raw bytes as a binary string, i.e. ''%%File.open("rb")%%'' then ''read()''. The format is whatever the //client's// %%libsndfile%% decodes: **WAV**, **VOC**, **OGG/Opus** and **FLAC** work everywhere; **MP3** only where the client's %%libsndfile%% is 1.1.0 or newer. Prefer OGG or WAV for portability -- there is no per-format capability probe.

==== Playing audio ====

The convenience functions handle slot and channel allocation and upload each sound only once per session:

  * ''play_sound(name, data, opts)'' -- play a one-shot sound-effect file. ''opts'': ''vol'' (0..100, default 100), ''pan'' (-100 left .. 0 center .. +100 right), ''loop'', ''ch''. Returns ''{ slot, ch }''.
  * ''play_tone(freq, ms, opts)'' -- play a synthesized tone (no file needed). ''opts'': ''shape'' (''%%"SINE"%%'', ''%%"SQUARE"%%'', ''%%"SAWTOOTH"%%'', ...), ''vol'', ''pan'', ''ch''.
  * ''play_music(name, data, opts)'' -- play a sound file looped on a reserved music channel. Returns a handle ''{ volume: function(0..100), stop: function([fade_ms]) }'' for a live volume slider and stop/fade-out.

<code javascript>
var cterm = load({}, 'cterm_lib.js');

if (cterm.supports_audio_files()) {
    // one-shot sound effect
    var f = new File(js.exec_dir + "beep.wav");
    f.open("rb"); var beep = f.read(); f.close();
    cterm.play_sound("beep", beep);

    // looping background music with a live volume control
    f = new File(js.exec_dir + "theme.ogg");
    f.open("rb"); var theme = f.read(); f.close();
    var music = cterm.play_music("theme", theme, { vol: 60 });
    // later:  music.volume(30);  music.stop(500 /* ms fade-out */);
} else if (cterm.supports_audio()) {
    cterm.play_tone(440, 250);      // a 440 Hz beep, no sound file required
}
</code>

==== Low-level control ====

The convenience functions above are built from one-verb primitives, available when you want to manage slots and channels yourself (e.g. to preload a bank of effects):

^ Function ^ Purpose ^
| ''audio_store(name, data)''            | cache a sound file under ''name'' |
| ''audio_load(slot, name)''             | decode the cached ''name'' into patch ''slot'' (0..255) |
| ''audio_queue(ch, slot, opts)''        | play ''slot'' on channel ''ch'' (2..15); ''opts'': ''vol''/''pan''/''loop'' |
| ''audio_volume(ch, vol)''              | set a channel's live volume (adjusts a sound already playing) |
| ''audio_volume_lr(ch, vl, vr)''        | set a channel's live per-side volume (stereo balance) |
| ''audio_synth(slot, shape, freq, ms)'' | synthesize a tone into ''slot'' (works without %%libsndfile%%) |
| ''audio_flush(ch, fade_ms)''           | stop a channel, with an optional fade-out |

===== See Also =====
  * [[:custom:javascript:lib:|JavaScript Libraries]]
  * [[:custom:javascript:lib:fonts.js|fonts.js]] library
  * [[http://syncterm.net|SyncTERM]]
  * [[https://en.wikipedia.org/wiki/Sixel|Sixel]]

{{tag>javascript syncterm cterm audio fonts palette}}
