diff --git a/bruhanimate/__init__.py b/bruhanimate/__init__.py index d5c73ca..093003a 100644 --- a/bruhanimate/__init__.py +++ b/bruhanimate/__init__.py @@ -1,3 +1,11 @@ +import sys + + +if sys.platform == "win32": + from .bruheffect import AudioEffect + from .demos import audio_demo + + from .bruheffect import ( BaseEffect, StaticEffect, @@ -45,7 +53,7 @@ FLAKE_FLIPS, TWINKLE_COLORS, FLAKES, - VALID_INTERFACES + VALID_INTERFACES, ) from .bruhrenderer import ( @@ -70,13 +78,12 @@ rain_demo, offset_demo, static_demo, - audio_demo, chatbot_demo, ) -__version__ = "0.2.62" + +__version__ = "0.2.64" __valid_demos__ = [ - "audio_demo", "static_demo", "offset_demo", "matrix_demo", @@ -90,70 +97,144 @@ "noise_demo" "holiday", ] -__all__ = [ - "Screen", - "plasma_demo", - "line_demo", - "holiday", - "snow_demo", - "stars_demo", - "twinkle_demo", - "noise_demo", - "matrix_demo", - "gol_demo", - "rain_demo", - "offset_demo", - "static_demo", - "audio_demo", - "chatbot_demo", - "BaseEffect", - "StaticEffect", - "OffsetEffect", - "NoiseEffect", - "StarEffect", - "PlasmaEffect", - "GameOfLifeEffect", - "RainEffect", - "MatrixEffect", - "ChatbotEffect", - "StringStreamer", - "GradientNoise", - "Loading", - "Key", - "DrawLinesEffect", - "Line", - "FLAKE", - "SnowEffect", - "Buffer", - "BaseRenderer", - "EffectRenderer", - "CenterRenderer", - "PanRenderer", - "FocusRenderer", - "BackgroundColorRenderer", - "images", - "__version__", - "__valid_demos__", - "LIFE_COLORS", - "LIFE_SCALES", - "PLASMA_COLORS", - "PLASMA_VALUES", - "GRADIENTS", - "VALID_DIRECTIONS", - "OLD_GREY_SCALES", - "GREY_SCALES", - "WIND_DIRECTIONS", - "NOISE", - "FLAKES", - "FLAKE_COLORS", - "FLAKE_JUMPS", - "NEXT_FLAKE_MOVE", - "FLAKE_WEIGHT_CHARS", - "FLAKE_FLIPS", - "TWINKLE_COLORS", - "VALID_INTERFACES", - "get_image", - "text_to_image", - "TwinkleEffect", - "TWINKLE_SPEC" -] +if sys.platform == "win32": + __valid_demos__.append("audio_demo") + + +if sys.platform == "win32": + __all__ = [ + "Screen", + "plasma_demo", + "line_demo", + "holiday", + "snow_demo", + "stars_demo", + "twinkle_demo", + "noise_demo", + "matrix_demo", + "gol_demo", + "rain_demo", + "offset_demo", + "static_demo", + "audio_demo", + "chatbot_demo", + "AudioEffect", + "BaseEffect", + "StaticEffect", + "OffsetEffect", + "NoiseEffect", + "StarEffect", + "PlasmaEffect", + "GameOfLifeEffect", + "RainEffect", + "MatrixEffect", + "ChatbotEffect", + "StringStreamer", + "GradientNoise", + "Loading", + "Key", + "DrawLinesEffect", + "Line", + "FLAKE", + "SnowEffect", + "Buffer", + "BaseRenderer", + "EffectRenderer", + "CenterRenderer", + "PanRenderer", + "FocusRenderer", + "BackgroundColorRenderer", + "images", + "__version__", + "__valid_demos__", + "LIFE_COLORS", + "LIFE_SCALES", + "PLASMA_COLORS", + "PLASMA_VALUES", + "GRADIENTS", + "VALID_DIRECTIONS", + "OLD_GREY_SCALES", + "GREY_SCALES", + "WIND_DIRECTIONS", + "NOISE", + "FLAKES", + "FLAKE_COLORS", + "FLAKE_JUMPS", + "NEXT_FLAKE_MOVE", + "FLAKE_WEIGHT_CHARS", + "FLAKE_FLIPS", + "TWINKLE_COLORS", + "VALID_INTERFACES", + "get_image", + "text_to_image", + "TwinkleEffect", + "TWINKLE_SPEC", + ] +else: + __all__ = [ + "Screen", + "plasma_demo", + "line_demo", + "holiday", + "snow_demo", + "stars_demo", + "twinkle_demo", + "noise_demo", + "matrix_demo", + "gol_demo", + "rain_demo", + "offset_demo", + "static_demo", + "chatbot_demo", + "BaseEffect", + "StaticEffect", + "OffsetEffect", + "NoiseEffect", + "StarEffect", + "PlasmaEffect", + "GameOfLifeEffect", + "RainEffect", + "MatrixEffect", + "ChatbotEffect", + "StringStreamer", + "GradientNoise", + "Loading", + "Key", + "DrawLinesEffect", + "Line", + "FLAKE", + "SnowEffect", + "Buffer", + "BaseRenderer", + "EffectRenderer", + "CenterRenderer", + "PanRenderer", + "FocusRenderer", + "BackgroundColorRenderer", + "images", + "__version__", + "__valid_demos__", + "LIFE_COLORS", + "LIFE_SCALES", + "PLASMA_COLORS", + "PLASMA_VALUES", + "GRADIENTS", + "VALID_DIRECTIONS", + "OLD_GREY_SCALES", + "GREY_SCALES", + "WIND_DIRECTIONS", + "NOISE", + "FLAKES", + "FLAKE_COLORS", + "FLAKE_JUMPS", + "NEXT_FLAKE_MOVE", + "FLAKE_WEIGHT_CHARS", + "FLAKE_FLIPS", + "TWINKLE_COLORS", + "VALID_INTERFACES", + "get_image", + "text_to_image", + "TwinkleEffect", + "TWINKLE_SPEC", + ] + diff --git a/bruhanimate/bruheffect/__init__.py b/bruhanimate/bruheffect/__init__.py index 1f120ac..6dd93eb 100644 --- a/bruhanimate/bruheffect/__init__.py +++ b/bruhanimate/bruheffect/__init__.py @@ -1,3 +1,4 @@ +import sys from .base_effect import BaseEffect from .static_effect import StaticEffect from .star_effect import StarEffect @@ -10,7 +11,6 @@ from .twinkle_effect import TwinkleEffect, TWINKLE_SPEC from .offset_effect import OffsetEffect from .rain_effect import RainEffect -from .audio_effect import AudioEffect from .noise_effect import NoiseEffect @@ -27,7 +27,6 @@ "TwinkleEffect", "OffsetEffect", "RainEffect", - "AudioEffect", "NoiseEffect", "GradientNoise", "Loading", @@ -35,4 +34,8 @@ "Key", "Line", "TWINKLE_SPEC" -] \ No newline at end of file +] + +if sys.platform == 'win32': + from .audio_effect import AudioEffect + __all__.append("AudioEffect") \ No newline at end of file diff --git a/bruhanimate/bruheffect/audio_effect.py b/bruhanimate/bruheffect/audio_effect.py index fe06f71..69c9d97 100644 --- a/bruhanimate/bruheffect/audio_effect.py +++ b/bruhanimate/bruheffect/audio_effect.py @@ -13,177 +13,178 @@ See the License for the specific language governing permissions and limitations under the License. """ - -import pyaudio -import random - -import numpy as np - -from bruhcolor import bruhcolored -from .base_effect import BaseEffect - - -class AudioEffect(BaseEffect): - def __init__( - self, buffer, background: str, num_bands: int = 24, audio_halt: int = 10 - ): - super(AudioEffect, self).__init__(buffer, background) - self.FORMAT = pyaudio.paInt16 - self.CHANNELS = 1 - self.RATE = 44100 - self.CHUNK = 1024 - self.BANDS = num_bands + 1 - self.audio_halt = audio_halt - self.bands = [] - self.p = pyaudio.PyAudio() - self.upper_band_bound = self.buffer.height() - self.band_ranges = self.generate_even_ranges(self.BANDS, 0, self.buffer.width()) - self.colors = [random.randint(0, 255) for _ in range(self.BANDS)] - self.stream = self.p.open( - format=self.FORMAT, - channels=self.CHANNELS, - rate=self.RATE, - input=True, - frames_per_buffer=self.CHUNK, - stream_callback=self.process_audio, - ) - self.gradient_mode = "extend" - self.base_gradient = [ - 232, - 233, - 235, - 237, - 239, - 241, - 243, - 245, - 247, - 249, - 251, - 253, - 255, - ] - self.gradient = [] - for _ in range(self.BANDS): - self.gradient += self.base_gradient - while len(self.gradient) < self.buffer.width(): - self.gradient += self.base_gradient - self.true_gradient = [self.gradient[idx] for idx in range(self.buffer.width())] - self.use_gradient = True - self.non_gradient_color = 27 - self.orientation = "top" - self.subtract_y = self.buffer.height() - - def set_audio_properties( - self, num_bands=24, audio_halt=10, use_gradient=True, non_gradient_color=27 - ): - self.BANDS = num_bands - self.audio_halt = audio_halt - self.band_ranges = self.generate_even_ranges(self.BANDS, 0, self.buffer.width()) - self.colors = [random.randint(0, 255) for _ in range(self.BANDS)] - self.use_gradient = use_gradient - self.non_gradient_color = non_gradient_color - - def evenly_distribute_original_values(self, original_list, desired_width): - repeat_count = desired_width // len(original_list) - extra_elements = desired_width % len(original_list) - expanded_list = [] - for value in original_list: - expanded_list.extend([value] * repeat_count) - if extra_elements > 0: - expanded_list.append(value) - extra_elements -= 1 - return expanded_list - - def set_orientation(self, orientation): - if orientation in ["top", "bottom"]: - self.orientation = orientation - if self.orientation == "bottom": - self.subtract_y = self.buffer.height() - else: - self.subtract_y = 0 - - def set_audio_gradient( - self, - gradient=[232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255], - mode="extend", - ): - self.base_gradient = gradient - self.gradient_mode = mode - if self.gradient_mode == "repeat": +import sys +if sys.platform == "win32": + import pyaudio + import random + + import numpy as np + + from bruhcolor import bruhcolored + from .base_effect import BaseEffect + + + class AudioEffect(BaseEffect): + def __init__( + self, buffer, background: str, num_bands: int = 24, audio_halt: int = 10 + ): + super(AudioEffect, self).__init__(buffer, background) + self.FORMAT = pyaudio.paInt16 + self.CHANNELS = 1 + self.RATE = 44100 + self.CHUNK = 1024 + self.BANDS = num_bands + 1 + self.audio_halt = audio_halt + self.bands = [] + self.p = pyaudio.PyAudio() + self.upper_band_bound = self.buffer.height() + self.band_ranges = self.generate_even_ranges(self.BANDS, 0, self.buffer.width()) + self.colors = [random.randint(0, 255) for _ in range(self.BANDS)] + self.stream = self.p.open( + format=self.FORMAT, + channels=self.CHANNELS, + rate=self.RATE, + input=True, + frames_per_buffer=self.CHUNK, + stream_callback=self.process_audio, + ) + self.gradient_mode = "extend" + self.base_gradient = [ + 232, + 233, + 235, + 237, + 239, + 241, + 243, + 245, + 247, + 249, + 251, + 253, + 255, + ] self.gradient = [] for _ in range(self.BANDS): self.gradient += self.base_gradient while len(self.gradient) < self.buffer.width(): self.gradient += self.base_gradient - self.true_gradient = [ - self.gradient[idx] for idx in range(self.buffer.width()) - ] - else: - self.gradient = self.evenly_distribute_original_values( - gradient, self.buffer.width() - ) - self.true_gradient = [ - self.gradient[idx] for idx in range(self.buffer.width()) + self.true_gradient = [self.gradient[idx] for idx in range(self.buffer.width())] + self.use_gradient = True + self.non_gradient_color = 27 + self.orientation = "top" + self.subtract_y = self.buffer.height() + + def set_audio_properties( + self, num_bands=24, audio_halt=10, use_gradient=True, non_gradient_color=27 + ): + self.BANDS = num_bands + self.audio_halt = audio_halt + self.band_ranges = self.generate_even_ranges(self.BANDS, 0, self.buffer.width()) + self.colors = [random.randint(0, 255) for _ in range(self.BANDS)] + self.use_gradient = use_gradient + self.non_gradient_color = non_gradient_color + + def evenly_distribute_original_values(self, original_list, desired_width): + repeat_count = desired_width // len(original_list) + extra_elements = desired_width % len(original_list) + expanded_list = [] + for value in original_list: + expanded_list.extend([value] * repeat_count) + if extra_elements > 0: + expanded_list.append(value) + extra_elements -= 1 + return expanded_list + + def set_orientation(self, orientation): + if orientation in ["top", "bottom"]: + self.orientation = orientation + if self.orientation == "bottom": + self.subtract_y = self.buffer.height() + else: + self.subtract_y = 0 + + def set_audio_gradient( + self, + gradient=[232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255], + mode="extend", + ): + self.base_gradient = gradient + self.gradient_mode = mode + if self.gradient_mode == "repeat": + self.gradient = [] + for _ in range(self.BANDS): + self.gradient += self.base_gradient + while len(self.gradient) < self.buffer.width(): + self.gradient += self.base_gradient + self.true_gradient = [ + self.gradient[idx] for idx in range(self.buffer.width()) + ] + else: + self.gradient = self.evenly_distribute_original_values( + gradient, self.buffer.width() + ) + self.true_gradient = [ + self.gradient[idx] for idx in range(self.buffer.width()) + ] + + def process_audio(self, data, frame_count, time_info, status): + audio_array = np.frombuffer(data, dtype=np.int16) + fft_result = np.fft.rfft(audio_array) + magnitudes = np.abs(fft_result) + band_width = len(magnitudes) // self.BANDS + self.bands = [ + np.mean(magnitudes[i * band_width : (i + 1) * band_width]) + for i in range(self.BANDS) ] - - def process_audio(self, data, frame_count, time_info, status): - audio_array = np.frombuffer(data, dtype=np.int16) - fft_result = np.fft.rfft(audio_array) - magnitudes = np.abs(fft_result) - band_width = len(magnitudes) // self.BANDS - self.bands = [ - np.mean(magnitudes[i * band_width : (i + 1) * band_width]) - for i in range(self.BANDS) - ] - return (data, pyaudio.paContinue) - - def map_bands_to_range(self, N): - min_band = min(self.bands) - max_band = max(self.bands) - rand_band = max_band - min_band if max_band != min_band else 1 - normalized_bands = [(band - min_band) / rand_band for band in self.bands] - scaled_bands = [int(band * N) for band in normalized_bands] - return scaled_bands - - def generate_even_ranges(self, groups, start, end): - approximate_group_size = round((end - start) / groups) - intervals = [] - for i in range(groups): - group_start = start + i * approximate_group_size - group_end = group_start + approximate_group_size - intervals.append((group_start, min(group_end, end))) - return intervals - - def render_frame(self, frame_number): - if frame_number == 0: - self.stream.start_stream() - - if frame_number % self.audio_halt == 0: - try: - self.buffer.clear_buffer() - mapped_bands = self.map_bands_to_range(self.upper_band_bound)[1:] - for idx, band_group in enumerate(zip(mapped_bands, self.band_ranges)): - band, band_range = band_group - for y_change in range(band): - for x_change in range(*band_range): - if self.use_gradient: - self.buffer.put_char( - x_change, - abs(self.subtract_y - y_change), - bruhcolored( - " ", on_color=self.true_gradient[x_change] - ).colored, - False, - ) - else: - self.buffer.put_char( - x_change, - abs(self.subtract_y - y_change), - bruhcolored( - " ", on_color=self.non_gradient_color - ).colored, - False, - ) - except: - pass + return (data, pyaudio.paContinue) + + def map_bands_to_range(self, N): + min_band = min(self.bands) + max_band = max(self.bands) + rand_band = max_band - min_band if max_band != min_band else 1 + normalized_bands = [(band - min_band) / rand_band for band in self.bands] + scaled_bands = [int(band * N) for band in normalized_bands] + return scaled_bands + + def generate_even_ranges(self, groups, start, end): + approximate_group_size = round((end - start) / groups) + intervals = [] + for i in range(groups): + group_start = start + i * approximate_group_size + group_end = group_start + approximate_group_size + intervals.append((group_start, min(group_end, end))) + return intervals + + def render_frame(self, frame_number): + if frame_number == 0: + self.stream.start_stream() + + if frame_number % self.audio_halt == 0: + try: + self.buffer.clear_buffer() + mapped_bands = self.map_bands_to_range(self.upper_band_bound)[1:] + for idx, band_group in enumerate(zip(mapped_bands, self.band_ranges)): + band, band_range = band_group + for y_change in range(band): + for x_change in range(*band_range): + if self.use_gradient: + self.buffer.put_char( + x_change, + abs(self.subtract_y - y_change), + bruhcolored( + " ", on_color=self.true_gradient[x_change] + ).colored, + False, + ) + else: + self.buffer.put_char( + x_change, + abs(self.subtract_y - y_change), + bruhcolored( + " ", on_color=self.non_gradient_color + ).colored, + False, + ) + except: + pass diff --git a/setup.py b/setup.py index a2c4f1a..732d3bb 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ with codecs.open(os.path.join(here, "README.md"), encoding="utf-8") as fh: long_description = "\n" + fh.read() -VERSION = "0.2.62" +VERSION = "0.2.64" DESCRIPTION = 'ASCII Terminal Animation Package' LONG_DESCRIPTION = 'A package that allows for various animations in the terminal' @@ -27,13 +27,12 @@ "future", "bruhcolor", "pyfiglet", - "pyaudio", "numpy", "openai", "requests" ], extras_require={ - ':sys_platform == "win32"': ['pywin32'], + ':sys_platform == "win32"': ["pywin32", "pyaudio"], }, setup_requires=['setuptools_scm'], keywords=['python', 'terminal', 'terminal-animation', 'bruhanimate'],