Пример #1
0
class BandsAudioEffect(AudioReactiveEffect, GradientEffect):

    NAME = "Bands"

    CONFIG_SCHEMA = vol.Schema(
        {
            vol.Optional('band_count',
                         description='Number of bands',
                         default=6):
            vol.All(vol.Coerce(int), vol.Range(min=1, max=16)),
            vol.Optional('align',
                         description='Alignment of bands',
                         default='left'):
            vol.In(list(["left", "right", "invert", "center"])),
            vol.Optional('gradient_name',
                         description='Color gradient to display',
                         default='Spectral'):
            vol.In(list(GRADIENTS.keys())),
            vol.Optional('mirror',
                         description='Mirror the effect',
                         default=False):
            bool
        })

    def config_updated(self, config):
        # Create the filters used for the effect
        self._r_filter = self.create_filter(alpha_decay=0.05, alpha_rise=0.999)
        self.bkg_color = np.array(COLORS["black"], dtype=float)

    def audio_data_updated(self, data):
        # Grab the filtered and interpolated melbank data
        y = data.interpolated_melbank(self.pixel_count, filtered=False)
        filtered_y = data.interpolated_melbank(self.pixel_count, filtered=True)

        # Grab the filtered difference between the filtered melbank and the
        # raw melbank.
        r = self._r_filter.update(y - filtered_y)
        out = np.tile(r, (3, 1)).T
        out_clipped = np.clip(out, 0, 1)
        out_split = np.array_split(out_clipped,
                                   self._config["band_count"],
                                   axis=0)
        for i in range(self._config["band_count"]):
            band_width = len(out_split[i])
            color = self.get_gradient_color(i / self._config["band_count"])
            vol = int(out_split[i].max() * band_width)  # length (vol) of band
            out_split[i][:] = self.bkg_color
            if vol:
                out_split[i][:vol] = color
            if self._config["align"] == "center":
                out_split[i] = np.roll(out_split[i], (band_width - vol) // 2,
                                       axis=0)
            elif self._config["align"] == "invert":
                out_split[i] = np.roll(out_split[i], -vol // 2, axis=0)
            elif self._config["align"] == "right":
                out_split[i] = np.flip(out_split[i], axis=0)
            elif self._config["align"] == "left":
                pass

        self.pixels = np.vstack(out_split)
Пример #2
0
class BandsMatrixAudioEffect(AudioReactiveEffect, GradientEffect):
    NAME = "Bands Matrix"

    CONFIG_SCHEMA = vol.Schema({
        vol.Optional("band_count", description="Number of bands", default=6):
        vol.All(vol.Coerce(int), vol.Range(min=1, max=16)),
        vol.Optional(
            "gradient_name",
            description="Color gradient to display",
            default="Rainbow",
        ):
        vol.In(list(GRADIENTS.keys())),
        vol.Optional(
            "mirror",
            description="Mirror the effect",
            default=False,
        ):
        bool,
        vol.Optional(
            "flip_gradient",
            description="Flip Gradient",
            default=False,
        ):
        bool,
    })

    def config_updated(self, config):
        # Create the filters used for the effect
        self._r_filter = self.create_filter(alpha_decay=0.05, alpha_rise=0.999)
        self.bkg_color = np.array(COLORS["black"], dtype=float)
        self.flip_gradient = config["flip_gradient"]

    def audio_data_updated(self, data):
        # Grab the filtered and interpolated melbank data
        y = data.interpolated_melbank(self.pixel_count, filtered=False)
        filtered_y = data.interpolated_melbank(self.pixel_count, filtered=True)

        # Grab the filtered difference between the filtered melbank and the
        # raw melbank.
        r = self._r_filter.update(y - filtered_y)
        out = np.tile(r, (3, 1)).T
        out_clipped = np.clip(out, 0, 1)
        out_split = np.array_split(out_clipped,
                                   self._config["band_count"],
                                   axis=0)
        for i in range(self._config["band_count"]):
            band_width = len(out_split[i])
            volume = int(out_split[i].max() * band_width)
            out_split[i][volume:] = self.bkg_color
            for p in range(volume):
                gradient_value = (1 -
                                  p / band_width if self.flip_gradient else p /
                                  band_width)
                out_split[i][p] = self.get_gradient_color(gradient_value)

            if i % 2 != 0:
                out_split[i] = np.flip(out_split[i], axis=0)

        self.pixels = np.vstack(out_split)
class EQAudioEffect(AudioReactiveEffect, GradientEffect):

    NAME = "Equalizer"

    CONFIG_SCHEMA = vol.Schema(
        {
            vol.Optional('align',
                         description='Alignment of bands',
                         default='left'):
            vol.In(list(["left", "right", "invert", "center"])),
            vol.Optional('gradient_name',
                         description='Color gradient to display',
                         default='Spectral'):
            vol.In(list(GRADIENTS.keys())),
            vol.Optional('gradient_repeat',
                         description='Repeat the gradient into segments',
                         default=6):
            vol.All(vol.Coerce(int), vol.Range(min=1, max=16)),
            vol.Optional('mirror',
                         description='Mirror the effect',
                         default=False):
            bool,
        })

    def config_updated(self, config):
        # Create the filters used for the effect
        self._r_filter = self.create_filter(alpha_decay=0.5, alpha_rise=0.1)

    def audio_data_updated(self, data):
        # Grab the filtered and interpolated melbank data
        y = data.interpolated_melbank(self.pixel_count, filtered=False)
        filtered_y = data.interpolated_melbank(self.pixel_count, filtered=True)

        # Grab the filtered difference between the filtered melbank and the
        # raw melbank.
        r = self._r_filter.update(y - filtered_y)
        r_clipped = np.clip(r, 0, 1)
        r_split = np.array_split(r_clipped, self._config["gradient_repeat"])
        for i in range(self._config["gradient_repeat"]):
            band_width = len(r_split[i])
            volume = int(r_split[i].sum() *
                         band_width)  # length (volume) of band
            r_split[i][:] = 0
            if volume:
                r_split[i][:volume] = 1
            if self._config["align"] == "center":
                r_split[i] = np.roll(r_split[i], (band_width - volume) // 2,
                                     axis=0)
            elif self._config["align"] == "invert":
                r_split[i] = np.roll(r_split[i], -volume // 2, axis=0)
            elif self._config["align"] == "right":
                r_split[i] = np.flip(r_split[i], axis=0)
            elif self._config["align"] == "left":
                pass

        self.pixels = self.apply_gradient(np.hstack(r_split))
Пример #4
0
class ColorRainbowEffect(Effect):

    CONFIG_SCHEMA = vol.Schema({
        vol.Optional('gradient_name',
                     description='Preset gradient name',
                     default='Spectral'):
        vol.In(list(GRADIENTS.keys())),
        vol.Optional('gradient_roll',
                     description='Amount to shift the gradient',
                     default=0):
        vol.Coerce(int),
        vol.Optional('gradient_method',
                     description='Function used to generate gradient',
                     default='cubic_ease'):
        vol.In(["cubic_ease", "bezier"]),
    })

    def apply_rainbow(self, dt):
        output = np.zeros(shape=(self.pixel_count, 3))
        if dt:
            rainbow = []
            rainbow.append([255, 0, 0])
            rainbow.append([255, 165, 0])
            rainbow.append([255, 255, 0])
            rainbow.append([0, 128, 0])
            rainbow.append([0, 0, 255])
            rainbow.append([75, 00, 130])
            rainbow.append([238, 130, 238])
            pixels_per_color = int(self.pixel_count / len(rainbow))
            for i in range(self.pixel_count):
                index = int(i / pixels_per_color)
                if index >= len(rainbow):
                    index = len(rainbow) - 1
                output[i] = [
                    rainbow[index][0], rainbow[index][1], rainbow[index][2]
                ]
                self.colormap[i] = output[i]
        else:
            for i in range(len(self.colormap)):
                for j in range(3):
                    self.colormap[i][j] -= 1
                    if self.colormap[i][j] < 0:
                        self.colormap[i][j] = 0
            output[i] = [
                self.colormap[i][0], self.colormap[i][1], self.colormap[i][2]
            ]
        return output
Пример #5
0
class Strobe(AudioReactiveEffect, GradientEffect):

    NAME = "Real Strobe"
    CONFIG_SCHEMA = vol.Schema({
        vol.Optional(
            "gradient_name",
            description="Color scheme for bass strobe to cycle through",
            default="Dancefloor",
        ):
        vol.In(list(GRADIENTS.keys())),
        vol.Optional(
            "color_step",
            description="Amount of color change per bass strobe",
            default=0.0625,
        ):
        vol.All(vol.Coerce(float), vol.Range(min=0, max=0.25)),
        vol.Optional(
            "bass_threshold",
            description="Cutoff for quiet sounds. Higher -> only loud sounds are detected",
            default=0.4,
        ):
        vol.All(vol.Coerce(float), vol.Range(min=0, max=1)),
        vol.Optional(
            "bass_strobe_decay_rate",
            description="Bass strobe decay rate. Higher -> decays faster.",
            default=0.5,
        ):
        vol.All(vol.Coerce(float), vol.Range(min=0, max=1)),
        vol.Optional(
            "strobe_color",
            description="Colour for note strobes",
            default="white",
        ):
        vol.In(list(COLORS.keys())),
        vol.Optional(
            "strobe_width",
            description="Note strobe width, in pixels",
            default=10,
        ):
        vol.All(vol.Coerce(int), vol.Range(min=0, max=1000)),
        vol.Optional(
            "strobe_decay_rate",
            description="Note strobe decay rate. Higher -> decays faster.",
            default=0.5,
        ):
        vol.All(vol.Coerce(float), vol.Range(min=0, max=1)),
    })

    def activate(self, pixel_count):
        super().activate(pixel_count)
        self.strobe_overlay = np.zeros(np.shape(self.pixels))
        self.bass_strobe_overlay = np.zeros(np.shape(self.pixels))
        self.onsets_queue = queue.Queue()

    def config_updated(self, config):
        self.bass_threshold = self._config["bass_threshold"]
        self.color_shift_step = self._config["color_step"]

        self.strobe_color = np.array(COLORS[self._config["strobe_color"]],
                                     dtype=float)
        self.last_color_shift_time = 0
        self.strobe_width = self._config["strobe_width"]
        self.color_shift_delay_in_seconds = 1
        self.color_idx = 0

        self.last_strobe_time = 0
        self.strobe_wait_time = 0
        self.strobe_decay_rate = 1 - self._config["strobe_decay_rate"]

        self.last_bass_strobe_time = 0
        self.bass_strobe_wait_time = 0
        self.bass_strobe_decay_rate = (1 -
                                       self._config["bass_strobe_decay_rate"])

    def get_pixels(self):
        pixels = np.copy(self.bass_strobe_overlay)

        if not self.onsets_queue.empty():
            self.onsets_queue.get()
            strobe_width = min(self.strobe_width, self.pixel_count)
            length_diff = self.pixel_count - strobe_width
            position = (0 if length_diff == 0 else
                        np.random.randint(self.pixel_count - strobe_width))
            self.strobe_overlay[position:position +
                                strobe_width] = self.strobe_color

        pixels += self.strobe_overlay

        self.strobe_overlay *= self.strobe_decay_rate
        self.bass_strobe_overlay *= self.bass_strobe_decay_rate
        self.pixels = pixels
        return self.pixels

    def audio_data_updated(self, data):
        self._dirty = True

        currentTime = time.time()

        if (currentTime - self.last_color_shift_time >
                self.color_shift_delay_in_seconds):
            self.color_idx += self.color_shift_step
            self.color_idx = self.color_idx % 1
            self.bass_strobe_color = self.get_gradient_color(self.color_idx)
            self.last_color_shift_time = currentTime

        lows_intensity = np.mean(data.melbank_lows())
        if (lows_intensity > self.bass_threshold
                and currentTime - self.last_bass_strobe_time >
                self.bass_strobe_wait_time):
            self.bass_strobe_overlay = np.tile(self.bass_strobe_color,
                                               (self.pixel_count, 1))
            self.last_bass_strobe_time = currentTime

        onsets = data.onset()
        if (onsets["high"] and
                currentTime - self.last_strobe_time > self.strobe_wait_time):
            self.onsets_queue.put(True)
            self.last_strobe_time = currentTime
Пример #6
0
class GradientEffect(Effect):
    """
    Simple effect base class that supplies gradient functionality. This
    is intended for effect which instead of outputing exact colors output
    colors based upon some configured color pallet.
    """

    CONFIG_SCHEMA = vol.Schema({
        vol.Optional('gradient_name',
                     description='Preset gradient name',
                     default='Spectral'):
        vol.In(list(GRADIENTS.keys())),
        vol.Optional('gradient_roll',
                     description='Amount to shift the gradient',
                     default=0):
        vol.Coerce(int),
        vol.Optional('gradient_method',
                     description='Function used to generate gradient',
                     default='cubic_ease'):
        vol.In(["cubic_ease", "bezier"]),
    })

    _gradient_curve = None

    def _comb(self, N, k):
        N = int(N)
        k = int(k)

        if k > N or N < 0 or k < 0:
            return 0

        M = N + 1
        nterms = min(k, N - k)
        numerator = 1
        denominator = 1

        for j in range(1, nterms + 1):
            numerator *= M - j
            denominator *= j

        return numerator // denominator

    def _bernstein_poly(self, i, n, t):
        """The Bernstein polynomial of n, i as a function of t"""
        return self._comb(n, i) * (t**(n - i)) * (1 - t)**i

    def _ease(self, chunk_len, start_val, end_val, slope=1.5):
        x = np.linspace(0, 1, chunk_len)
        diff = end_val - start_val
        pow_x = np.power(x, slope)
        return diff * pow_x / (pow_x + np.power(1 - x, slope)) + start_val

    def _color_ease(self, chunk_len, start_color, end_color):
        """Makes a coloured block easing from start to end colour"""
        return np.array([
            self._ease(chunk_len, start_color[i], end_color[i])
            for i in range(3)
        ])

    def _generate_gradient_curve(self, gradient_colors, gradient_method,
                                 gradient_length):

        # Check to see if we have a custom gradient, or a predefined one and
        # load the colors accordingly
        if isinstance(gradient_colors, str):
            gradient_name = gradient_colors
            gradient_colors = []
            if GRADIENTS.get(gradient_name):
                gradient_colors = GRADIENTS.get(gradient_name).get("colors")
                gradient_method = GRADIENTS.get(gradient_name).get(
                    "method", gradient_method)
            elif COLORS.get(gradient_name):
                gradient_colors = [gradient_name]

        if not gradient_colors:
            gradient_colors = GRADIENTS.get('spectral')

        self.rgb_list = np.array(
            [COLORS[color.lower()] for color in gradient_colors]).T
        n_colors = len(self.rgb_list[0])

        if gradient_method == "bezier":
            t = np.linspace(0.0, 1.0, gradient_length)
            polynomial_array = np.array([
                self._bernstein_poly(i, n_colors - 1, t)
                for i in range(0, n_colors)
            ])
            polynomial_array = np.fliplr(polynomial_array)
            gradient = np.array([
                np.dot(self.rgb_list[0], polynomial_array),
                np.dot(self.rgb_list[1], polynomial_array),
                np.dot(self.rgb_list[2], polynomial_array)
            ])
            _LOGGER.info(
                ('Generating new gradient curve for {}'.format(gradient_colors)
                 ))
            self._gradient_curve = gradient

        elif gradient_method == "cubic_ease":
            t = np.zeros(gradient_length)
            ease_chunks = np.array_split(t, n_colors - 1)
            color_pairs = np.array([(self.rgb_list.T[i],
                                     self.rgb_list.T[i + 1])
                                    for i in range(n_colors - 1)])
            gradient = np.hstack(
                self._color_ease(len(ease_chunks[i]), *color_pairs[i])
                for i in range(n_colors - 1))
            _LOGGER.info(
                ('Generating new gradient curve for {}'.format(gradient_colors)
                 ))
            self._gradient_curve = gradient

        else:
            gradient = np.zeros((gradient_length, 3))
            for i in range(gradient_length):
                rgb_i = i % n_colors
                gradient[i] = (self.rgb_list[0][rgb_i],
                               self.rgb_list[1][rgb_i],
                               self.rgb_list[2][rgb_i])
            self._gradient_curve = gradient.T

    def _gradient_valid(self):
        if self._gradient_curve is None:
            return False  # Uninitialized gradient
        if len(self._gradient_curve[0]) != self.pixel_count:
            return False  # Incorrect size
        return True

    def _validate_gradient(self):
        if not self._gradient_valid():
            self._generate_gradient_curve(self._config['gradient_name'],
                                          self._config['gradient_method'],
                                          self.pixel_count)

    def _roll_gradient(self):
        if self._config['gradient_roll'] == 0:
            return

        self._gradient_curve = np.roll(self._gradient_curve,
                                       self._config['gradient_roll'],
                                       axis=1)

    def get_gradient_color(self, point):
        self._validate_gradient()

        return self._gradient_curve[:, point]

        #n_colors = len(self.rgb_list[0])
        #polynomial_array = np.array([self._bernstein_poly(i, n_colors-1, point) for i in range(0, n_colors)])
        #return (np.dot(self.rgb_list[0], polynomial_array),
        #        np.dot(self.rgb_list[1], polynomial_array),
        #        np.dot(self.rgb_list[2], polynomial_array))

    def config_updated(self, config):
        """Invalidate the gradient"""
        self._gradient_curve = None

    def apply_gradient(self, y):
        self._validate_gradient()

        # Apply and roll the gradient if necessary
        output = (self._gradient_curve[:][::1] * y).T
        self._roll_gradient()

        return output
Пример #7
0
class BarAudioEffect(AudioReactiveEffect, GradientEffect):

    NAME = "Bar"
    CONFIG_SCHEMA = vol.Schema({
        vol.Optional('gradient_name', description='Color scheme to cycle through', default='Spectral'): vol.In(list(GRADIENTS.keys())),
        vol.Optional('mode', description='Choose from different animations', default='wipe'): vol.In(list(["bounce", "wipe", "in-out"])),
        vol.Optional('ease_method', description='Acceleration profile of bar', default='ease_out'): vol.In(list(["ease_in_out", "ease_in", "ease_out", "linear"])),
        vol.Optional('color_step', description='Amount of color change per beat', default=0.125): vol.All(vol.Coerce(float), vol.Range(min=0.0625, max=0.5))
    })

    def config_updated(self, config):
        self.phase = 0
        self.color_idx = 0
        self.bar_len = 0.3

    def audio_data_updated(self, data):
        # Run linear beat oscillator through easing method
        beat_oscillator, beat_now = data.oscillator()
        if self._config["ease_method"] == "ease_in_out":
            x = 0.5*np.sin(np.pi*(beat_oscillator-0.5))+0.5
        elif self._config["ease_method"] == "ease_in":
            x = beat_oscillator**2
        elif self._config["ease_method"] == "ease_out":
            x = -(beat_oscillator-1)**2+1
        elif self._config["ease_method"] == "linear":
            x = beat_oscillator

        # Colour change and phase
        if beat_now:
            self.phase = 1-self.phase  # flip flop 0->1, 1->0
            if self.phase == 0:
                # 8 colours, 4 beats to a bar
                self.color_idx += self._config["color_step"]
                self.color_idx = self.color_idx % 1  # loop back to zero

        # Compute position of bar start and stop
        if self._config["mode"] == "wipe":
            if self.phase == 0:
                bar_end = x
                bar_start = 0
            elif self.phase == 1:
                bar_end = 1
                bar_start = x

        elif self._config["mode"] == "bounce":
            x = x*(1-self.bar_len)
            if self.phase == 0:
                bar_end = x+self.bar_len
                bar_start = x
            elif self.phase == 1:
                bar_end = 1-x
                bar_start = 1-(x+self.bar_len)

        elif self._config["mode"] == "in-out":
            if self.phase == 0:
                bar_end = x
                bar_start = 0
            elif self.phase == 1:
                bar_end = 1-x
                bar_start = 0

        # Construct the bar
        color = self.get_gradient_color(self.color_idx)
        image = Image.new("RGB", (self.pixel_count, 1), color=0)
        d = ImageDraw.Draw(image)
        d.rectangle(((int(self.pixel_count*bar_start), 0),
                     (int(self.pixel_count*bar_end), 1)), fill=tuple(color.astype('B')))

        # Update the pixel values
        self.pixels = image
Пример #8
0
class BarAudioEffect(AudioReactiveEffect, GradientEffect):

    NAME = "Bar"
    CONFIG_SCHEMA = vol.Schema({
        vol.Optional(
            "gradient_name",
            description="Color scheme to cycle through",
            default="Rainbow",
        ):
        vol.In(list(GRADIENTS.keys())),
        vol.Optional(
            "mode",
            description="Choose from different animations",
            default="wipe",
        ):
        vol.In(list(["bounce", "wipe", "in-out"])),
        vol.Optional(
            "ease_method",
            description="Acceleration profile of bar",
            default="ease_out",
        ):
        vol.In(list(["ease_in_out", "ease_in", "ease_out", "linear"])),
        vol.Optional(
            "color_step",
            description="Amount of color change per beat",
            default=0.125,
        ):
        vol.All(vol.Coerce(float), vol.Range(min=0.0625, max=0.5)),
    })

    def config_updated(self, config):
        self.phase = 0
        self.color_idx = 0
        self.bar_len = 0.3

    def audio_data_updated(self, data):
        # Run linear beat oscillator through easing method
        beat_oscillator, beat_now = data.oscillator()
        if self._config["ease_method"] == "ease_in_out":
            x = 0.5 * np.sin(np.pi * (beat_oscillator - 0.5)) + 0.5
        elif self._config["ease_method"] == "ease_in":
            x = beat_oscillator**2
        elif self._config["ease_method"] == "ease_out":
            x = -((beat_oscillator - 1)**2) + 1
        elif self._config["ease_method"] == "linear":
            x = beat_oscillator

        # Colour change and phase
        if beat_now:
            self.phase = 1 - self.phase  # flip flop 0->1, 1->0
            if self.phase == 0:
                # 8 colours, 4 beats to a bar
                self.color_idx += self._config["color_step"]
                self.color_idx = self.color_idx % 1  # loop back to zero

        # Compute position of bar start and stop
        if self._config["mode"] == "wipe":
            if self.phase == 0:
                bar_end = x
                bar_start = 0
            elif self.phase == 1:
                bar_end = 1
                bar_start = x

        elif self._config["mode"] == "bounce":
            x = x * (1 - self.bar_len)
            if self.phase == 0:
                bar_end = x + self.bar_len
                bar_start = x
            elif self.phase == 1:
                bar_end = 1 - x
                bar_start = 1 - (x + self.bar_len)

        elif self._config["mode"] == "in-out":
            if self.phase == 0:
                bar_end = x
                bar_start = 0
            elif self.phase == 1:
                bar_end = 1 - x
                bar_start = 0

        # Construct the bar
        color = self.get_gradient_color(self.color_idx)
        p = np.zeros(np.shape(self.pixels))
        p[int(self.pixel_count * bar_start):int(self.pixel_count *
                                                bar_end), :, ] = color

        # Update the pixel values
        self.pixels = p
Пример #9
0
class MultiBarAudioEffect(AudioReactiveEffect, GradientEffect):

    NAME = "Multicolor Bar"
    CONFIG_SCHEMA = vol.Schema({
        vol.Optional('gradient_name', description='Color scheme to cycle through', default = 'Spectral'): vol.In(list(GRADIENTS.keys())),
        vol.Optional('mode', description='Choose from different animations', default = 'wipe'): vol.In(list(["cascade", "wipe"])),
        vol.Optional('ease_method', description='Acceleration profile of bar', default='linear'): vol.In(list(["ease_in_out", "ease_in", "ease_out", "linear"])),
        vol.Optional('color_step', description='Amount of color change per beat', default = 0.125): vol.All(vol.Coerce(float), vol.Range(min=0.0625, max=0.5))
    })

    def config_updated(self, config):
        self.phase = 0
        self.color_idx = 0

    def audio_data_updated(self, data):
        # Run linear beat oscillator through easing method
        beat_oscillator, beat_now = data.oscillator()
        if self._config["ease_method"] == "ease_in_out":
            x = 0.5*np.sin(np.pi*(beat_oscillator-0.5))+0.5
        elif self._config["ease_method"] == "ease_in":
            x = beat_oscillator**2
        elif self._config["ease_method"] == "ease_out":
            x = -(beat_oscillator-1)**2+1
        elif self._config["ease_method"] == "linear":
            x = beat_oscillator

        # Colour change and phase
        if beat_now:
            self.phase = 1-self.phase # flip flop 0->1, 1->0
            self.color_idx += self._config["color_step"] # 8 colours, 4 beats to a bar
            self.color_idx = self.color_idx % 1 # loop back to zero

        color_fg = self.get_gradient_color(self.color_idx)
        color_bkg = self.get_gradient_color((self.color_idx+self._config["color_step"])%1)

        # Compute position of bar start and stop
        if self._config["mode"] == "wipe":
            if self.phase == 0:
                idx = x
            elif self.phase == 1:
                idx = 1-x
                color_fg, color_bkg = color_bkg, color_fg

        elif self._config["mode"] == "cascade":
            idx = x

        # Construct the array
        p = np.zeros(np.shape(self.pixels))
        p[:int(self.pixel_count*idx), :] = color_bkg
        p[int(self.pixel_count*idx):, :] = color_fg
        # Update the pixel values
        self.pixels = p