diff --git a/adafruit_color_terminal.py b/adafruit_color_terminal.py index 933f249..17c3113 100644 --- a/adafruit_color_terminal.py +++ b/adafruit_color_terminal.py @@ -5,7 +5,8 @@ `adafruit_color_terminal` ================================================================================ -Extension of supports ANSI color escapes for subsets of text +Extension of supports ANSI color escapes for subsets of text and optionally the +ASCII bell escape code. * Author(s): Tim Cocks @@ -28,6 +29,7 @@ __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Color_Terminal.git" +from audiocore import WaveFile from displayio import Palette, TileGrid from terminalio import Terminal from tilepalettemapper import TilePaletteMapper @@ -63,9 +65,20 @@ class ColorTerminal: :param height: The height of the terminal in characters. :param custom_palette: A custom palette of colors to use instead of the default ones. Must contain at least 9 colors. + :param audio_interface: The audio interface to use for playing ASCII bell escape codes. + :param bell_audio_file: The wave audio file to use for the ASCII bell escape codes. + Defaults to beep.wav """ - def __init__(self, font, width, height, custom_palette=None): + def __init__( + self, + font, + width, + height, + custom_palette=None, + audio_interface=None, + bell_audio_file="/beep.wav", + ): if custom_palette is None: self.terminal_palette = Palette(9) self.terminal_palette[0] = 0x000000 @@ -99,6 +112,11 @@ def __init__(self, font, width, height, custom_palette=None): self.cur_color_mapping = [0, 1] + self.audio_interface = audio_interface + if audio_interface is not None: + beep_wave_file = open(bell_audio_file, "rb") + self.beep_wave = WaveFile(beep_wave_file) + @staticmethod def parse_ansi_colors(text): """ @@ -195,6 +213,13 @@ def write(self, s): if not color_map: self.terminal.write(s) + if ( + "\x07" in s + and self.audio_interface is not None + and not self.audio_interface.playing + ): + print("playing beep") + self.audio_interface.play(self.beep_wave) return idx = 0 @@ -222,6 +247,12 @@ def write(self, s): self.apply_color(cur_slice) self.terminal.write(cur_slice) + if ( + "\x07" in cur_slice + and self.audio_interface is not None + and not self.audio_interface.playing + ): + self.audio_interface.play(self.beep_wave) # index after last can be in the color map if color code is last thing in string if idx in color_map: diff --git a/docs/conf.py b/docs/conf.py index e38c932..ecd67bd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,7 +25,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = ["terminalio", "tilepalettemapper", "displayio"] +autodoc_mock_imports = ["terminalio", "tilepalettemapper", "displayio", "audiocore"] autodoc_preserve_defaults = True diff --git a/examples/beep.wav b/examples/beep.wav new file mode 100644 index 0000000..bdee4e4 Binary files /dev/null and b/examples/beep.wav differ diff --git a/examples/beep.wav.license b/examples/beep.wav.license new file mode 100644 index 0000000..99fbe5f --- /dev/null +++ b/examples/beep.wav.license @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense diff --git a/examples/color_terminal_fruit_jam_belltest.py b/examples/color_terminal_fruit_jam_belltest.py new file mode 100644 index 0000000..aa2d20c --- /dev/null +++ b/examples/color_terminal_fruit_jam_belltest.py @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +import time + +import supervisor +from adafruit_fruitjam import peripherals +from displayio import Group +from terminalio import FONT + +from adafruit_color_terminal import ColorTerminal + +main_group = Group() + +display = supervisor.runtime.display +if display is None or peripherals.get_display_config()[2] < 4: + print( + "To use ColorTerminal there must be an initialized display with a color depth at least 4." + ) + print( + "Initializing display to default size 640x480 with color depth 8, ", + "if you do not want this configuration then initialize the display ", + "before running this example.", + ) + peripherals.request_display_config(640, 480, 8) + +fruitjam_peripherals = peripherals.Peripherals() + +fruitjam_peripherals.dac.headphone_output = True +fruitjam_peripherals.dac.configure_clocks(sample_rate=16000, bit_depth=16) + +font_bb = FONT.get_bounding_box() +screen_size = (display.width // font_bb[0], display.height // font_bb[1]) + +terminal = ColorTerminal( + FONT, screen_size[0], screen_size[1], audio_interface=fruitjam_peripherals.audio +) +main_group.append(terminal.tilegrid) + +black = chr(27) + "[30m" +red = chr(27) + "[31m" +green = chr(27) + "[32m" +yellow = chr(27) + "[33m" +blue = chr(27) + "[34m" +magenta = chr(27) + "[35m" +cyan = chr(27) + "[36m" +white = chr(27) + "[37m" +reset = chr(27) + "[0m" + + +message = f"Hello {green}World{reset} {yellow}ANSI\n" +terminal.write(message) +print(message, end="") + +message = f"{magenta}Terminal {red}Colors{reset}" +terminal.write(message) +print(message) + +display.root_group = main_group + +print(terminal.cursor_x, terminal.cursor_y) + +move_cursor = chr(27) + "[10;10H" +terminal.write(f" Something {move_cursor}{cyan} Else{reset}") + +time.sleep(2) + +terminal.write(" beep\x07") + +while True: + pass diff --git a/examples/color_terminal_fruit_jam_test.py b/examples/color_terminal_fruit_jam_test.py new file mode 100644 index 0000000..80a488f --- /dev/null +++ b/examples/color_terminal_fruit_jam_test.py @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import supervisor +from adafruit_fruitjam import peripherals +from displayio import Group +from terminalio import FONT + +from adafruit_color_terminal import ColorTerminal + +main_group = Group() + +display = supervisor.runtime.display +if display is None or peripherals.get_display_config()[2] < 4: + print( + "To use ColorTerminal there must be an initialized display with a color depth at least 4." + ) + print( + "Initializing display to default size 640x480 with color depth 8, ", + "if you do not want this configuration then initialize the display ", + "before running this example.", + ) + peripherals.request_display_config(640, 480, 8) + +font_bb = FONT.get_bounding_box() +screen_size = (display.width // font_bb[0], display.height // font_bb[1]) + +terminal = ColorTerminal(FONT, screen_size[0], screen_size[1]) +main_group.append(terminal.tilegrid) + +black = chr(27) + "[30m" +red = chr(27) + "[31m" +green = chr(27) + "[32m" +yellow = chr(27) + "[33m" +blue = chr(27) + "[34m" +magenta = chr(27) + "[35m" +cyan = chr(27) + "[36m" +white = chr(27) + "[37m" +reset = chr(27) + "[0m" + + +message = f"Hello {green}World{reset} {yellow}ANSI\n" +terminal.write(message) +print(message, end="") + +message = f"{magenta}Terminal {red}Colors{reset}" +terminal.write(message) +print(message) + +display.root_group = main_group + +print(terminal.cursor_x, terminal.cursor_y) + +move_cursor = chr(27) + "[10;10H" +terminal.write(f" Something {move_cursor}{cyan} Else{reset}") + +while True: + pass diff --git a/ruff.toml b/ruff.toml index e9a8192..3c0f2b2 100644 --- a/ruff.toml +++ b/ruff.toml @@ -92,6 +92,8 @@ ignore = [ "PLR2004", # magic-value-comparison "UP030", # format literals "PLW1514", # unspecified-encoding + "PLR0913", # too many args + "PLR0917", # too many positional args ]