diff --git a/8k8bitpcm.wav b/8k8bitpcm.wav new file mode 100644 index 0000000..2e75fc3 Binary files /dev/null and b/8k8bitpcm.wav differ diff --git a/bootstrap.py b/bootstrap.py index 0211a7d..9fbfc33 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -5,10 +5,8 @@ and checks needed by any script """ -from time import sleep from raspledstrip.ledstrip import * -from raspledstrip.animation import * - +from raspledstrip.LPD8806 import LPD8806Native import os.path import sys @@ -42,8 +40,8 @@ """) sys.exit(2) -num = 36 * 10; -led = LEDStrip(num) +led_count = 36 +led = LEDStrip(LPD8806Native(led_count, dev)) #led.setChannelOrder(ChannelOrder.BRG) #Only use this if your strip does not use the GRB order #led.setMasterBrightness(0.5) #use this to set the overall max brightness of the strip led.all_off() \ No newline at end of file diff --git a/example.py b/example.py index 0e24157..24216e4 100755 --- a/example.py +++ b/example.py @@ -1,27 +1,28 @@ -#!/usr/bin/python +# !/usr/bin/python from bootstrap import * +from raspledstrip.animation import * #setup colors to loop through for fade colors = [ - (255.0,0.0,0.0), - (0.0,255.0,0.0), - (0.0,0.0,255.0), - (255.0,255.0,255.0), + (255.0, 0.0, 0.0), + (0.0, 255.0, 0.0), + (0.0, 0.0, 255.0), + (255.0, 255.0, 255.0), ] step = 0.01 for c in range(4): - r, g, b = colors[c] - level = 0.01 - dir = step - while level >= 0.0: - led.fill(Color(r, g, b, level)) - led.update() - if(level >= 0.99): - dir = -step - level += dir - #sleep(0.005) + r, g, b = colors[c] + level = 0.01 + dir = step + while level >= 0.0: + led.fill(Color(r, g, b, level)) + led.update() + if (level >= 0.99): + dir = -step + level += dir + #sleep(0.005) led.all_off() @@ -29,76 +30,76 @@ #after each step, call update() to push it to the LED strip #sin wave animations anim = Wave(led, Color(255, 0, 0), 4) -for i in range(led.lastIndex): - anim.step() - led.update() - #sleep(0.15) +for i in range(led.last_index): + anim.step() + led.update() +#sleep(0.15) anim = Wave(led, Color(0, 0, 100), 2) -for i in range(led.lastIndex): - anim.step() - led.update() - #sleep(0.15) +for i in range(led.last_index): + anim.step() + led.update() +#sleep(0.15) #rolling rainbow anim = Rainbow(led) for i in range(384): - anim.step() - led.update() + anim.step() + led.update() -led.fillOff() +led.fill_off() #evenly distributed rainbow anim = RainbowCycle(led) -for i in range(384*2): - anim.step() - led.update() +for i in range(384 * 2): + anim.step() + led.update() -led.fillOff() +led.fill_off() #setup colors for wipe and chase colors = [ - Color(255, 0, 0), - Color(0, 255, 0), - Color(0, 0, 255), - Color(255, 255, 255), + Color(255, 0, 0), + Color(0, 255, 0), + Color(0, 0, 255), + Color(255, 255, 255), ] for c in range(4): - anim = ColorWipe(led, colors[c]) + anim = ColorWipe(led, colors[c]) - for i in range(num): - anim.step() - led.update() - #sleep(0.03) + for i in range(led_count): + anim.step() + led.update() + #sleep(0.03) -led.fillOff() +led.fill_off() for c in range(4): - anim = ColorChase(led, colors[c]) + anim = ColorChase(led, colors[c]) - for i in range(num): - anim.step() - led.update() - #sleep(0.03) + for i in range(led_count): + anim.step() + led.update() + #sleep(0.03) -led.fillOff() +led.fill_off() #scanner: single color and changing color anim = LarsonScanner(led, Color(255, 0, 0)) -for i in range(led.lastIndex*4): - anim.step() - led.update() - #sleep(0.03) +for i in range(led.last_index * 4): + anim.step() + led.update() +#sleep(0.03) -led.fillOff() +led.fill_off() anim = LarsonRainbow(led, 2, 0.5) -for i in range(led.lastIndex*4): - anim.step() - led.update() - #sleep(0.03) +for i in range(led.last_index * 4): + anim.step() + led.update() +#sleep(0.03) led.all_off() diff --git a/raspledstrip/LPD8806.py b/raspledstrip/LPD8806.py index bc83eae..33354d5 100644 --- a/raspledstrip/LPD8806.py +++ b/raspledstrip/LPD8806.py @@ -1,39 +1,55 @@ -class LPD8806(object): +try: + import spidev +except ImportError: + pass + + +class LEDDriver(object): + def __init__(self, led_count): + self.led_count = led_count + + def get_led_count(self): + """ + Return the number of LEDs in this device + :rtype: :class:`int` + """ + return self.led_count + + +class LPD8806Native(LEDDriver): """Main driver for LPD8806 based LED strips""" - def __init__(self, leds, use_py_spi = False, dev="/dev/spidev0.0"): - self.leds = leds + def __init__(self, led_count, dev="/dev/spidev0.0"): + super(LPD8806Native, self).__init__(led_count) self.dev = dev - self.use_py_spi = use_py_spi - - if self.use_py_spi: - import spidev - self.spi = spidev.SpiDev() - self.spi.open(0,0) - self.spi.max_speed_hz = 12000000 - print 'py-spidev MHz: %d' % (self.spi.max_speed_hz / 1000000.0 ) - else: - self.spi = open(self.dev, "wb") + self.spi = open(self.dev, "wb") #Push new data to strand - def update(self, buffer): - if self.use_py_spi: - for x in range(self.leds): - self.spi.xfer2([i for i in buffer[x]]) - - self.spi.xfer2([0x00,0x00,0x00]) #zero fill the last to prevent stray colors at the end - self.spi.xfer2([0x00]) #once more with feeling - this helps :) - else: - for x in range(self.leds): - self.spi.write(buffer[x]) - self.spi.flush() - #seems that the more lights we have the more you have to push zeros - #not 100% sure why this is yet, but it seems to work - self.spi.write(bytearray(b'\x00\x00\x00')) #zero fill the last to prevent stray colors at the end - self.spi.flush() - self.spi.write(bytearray(b'\x00\x00\x00')) - self.spi.flush() - self.spi.write(bytearray(b'\x00\x00\x00')) - self.spi.flush() - self.spi.write(bytearray(b'\x00\x00\x00')) + def update(self, pixel_buffer): + for x in xrange(self.led_count): + self.spi.write(pixel_buffer[x]) self.spi.flush() + #seems that the more lights we have the more you have to push zeros + #not 100% sure why this is yet, but it seems to work + self.spi.write(bytearray(b'\x00\x00\x00')) #zero fill the last to prevent stray colors at the end + self.spi.flush() + self.spi.write(bytearray(b'\x00\x00\x00')) + self.spi.flush() + self.spi.write(bytearray(b'\x00\x00\x00')) + self.spi.flush() + self.spi.write(bytearray(b'\x00\x00\x00')) + self.spi.flush() + + +class LPD8806SPI(LEDDriver): + def __init__(self, led_count): + super(LPD8806SPI, self).__init__(led_count) + if not spidev: + raise Exception("SPI module not available") + self.spi = spidev.SpiDev() + self.spi.open(0, 0) + self.spi.max_speed_hz = 18000000 + print 'py-spidev MHz: %d' % (self.spi.max_speed_hz / 1000000.0) + + def update(self, pixel_buffer): + self.spi.xfer2([item for sublist in pixel_buffer for item in sublist]+[0x00, 0x00, 0x00]) diff --git a/raspledstrip/animation.py b/raspledstrip/animation.py index 4ae219f..ef9f5de 100644 --- a/raspledstrip/animation.py +++ b/raspledstrip/animation.py @@ -1,60 +1,62 @@ import math -import time +import random + from color import * import util -import random + class BaseAnimation(object): - def __init__(self, led, start, end): - self._led = led + def __init__(self, led_strip, start, end): + self._led = led_strip self._start = start self._end = end - if self._end == 0 or self._end > self._led.lastIndex: - self._end = self._led.lastIndex + if self._end == 0 or self._end > self._led.last_index: + self._end = self._led.last_index self._size = self._end - self._start + 1 self._step = 0 - self._timeRef = 0 + self._time_ref = 0 - def __msTime(self): + def __ms_time(self): return time.time() * 1000.0 - def step(self, amt = 1): + def step(self, amt=1): raise RuntimeError("Base class step() called. This shouldn't happen") - def run(self, amt = 1, sleep=None, max_steps = 0): - self._step = 0 - cur_step = 0 + def run(self, amt=1, sleep=None, max_steps=0): + cur_step = self._step while max_steps == 0 or cur_step < max_steps: - self._timeRef = self.__msTime() + self._time_ref = self.__ms_time() self.step(amt) self._led.update() if sleep: - diff = (self.__msTime() - self._timeRef) + diff = (self.__ms_time() - self._time_ref) t = max(0, (sleep - diff) / 1000.0) if t == 0: print "Timeout of %dms is less than the minimum of %d!" % (sleep, diff) time.sleep(t) cur_step += 1 + class Nothing(BaseAnimation): """Placeholder for killing time in animation scripts while keeping the LEDs off""" - def __init__(self, led, start=0, end=0): - super(Nothing, self).__init__(led, start, end) + def __init__(self, led_strip, start=0, end=0): + super(Nothing, self).__init__(led_strip, start, end) - def step(self, amt = 1): - self._led.fillOff(self._start, self._end) + def step(self, amt=1): + self._led.fill_off(self._start, self._end) self._step += amt + class Rainbow(BaseAnimation): """Generate rainbow.""" - def __init__(self, led, start=0, end=0): - super(Rainbow, self).__init__(led, start, end) + def __init__(self, led_strip, start=0, end=0): + super(Rainbow, self).__init__(led_strip, start, end) - def step(self, amt = 1): + def step(self, amt=1): for i in range(self._size): color = (i + self._step) % 384 @@ -65,13 +67,14 @@ def step(self, amt = 1): if overflow >= 0: self._step = overflow + class RainbowCycle(BaseAnimation): """Generate rainbow wheel equally distributed over strip.""" - def __init__(self, led, start=0, end=0): - super(RainbowCycle, self).__init__(led, start, end) + def __init__(self, led_strip, start=0, end=0): + super(RainbowCycle, self).__init__(led_strip, start, end) - def step(self, amt = 1): + def step(self, amt=1): for i in range(self._size): color = (i * (384 / self._size) + self._step) % 384 self._led.set(self._start + i, wheel_color(color)) @@ -81,20 +84,21 @@ def step(self, amt = 1): if overflow >= 0: self._step = overflow + class ColorPattern(BaseAnimation): """Fill the dots progressively along the strip with alternating colors.""" - def __init__(self, led, colors, width, dir = True, start=0, end=0): - super(ColorPattern, self).__init__(led, start, end) + def __init__(self, led_strip, colors, width, dir=True, start=0, end=0): + super(ColorPattern, self).__init__(led_strip, start, end) self._colors = colors self._colorCount = len(colors) self._width = width - self._total_width = self._width * self._colorCount; + self._total_width = self._width * self._colorCount self._dir = dir - def step(self, amt = 1): + def step(self, amt=1): for i in range(self._size): - cIndex = ((i+self._step) % self._total_width) / self._width; + cIndex = ((i + self._step) % self._total_width) / self._width self._led.set(self._start + i, self._colors[cIndex]) if self._dir: self._step += amt @@ -106,16 +110,17 @@ def step(self, amt = 1): if self._step < 0: self._step = self._end + self._step + class ColorWipe(BaseAnimation): """Fill the dots progressively along the strip.""" - def __init__(self, led, color, start=0, end=0): - super(ColorWipe, self).__init__(led, start, end) + def __init__(self, led_strip, color, start=0, end=0): + super(ColorWipe, self).__init__(led_strip, start, end) self._color = color - def step(self, amt = 1): + def step(self, amt=1): if self._step == 0: - self._led.fillOff() + self._led.fill_off() for i in range(amt): self._led.set(self._start + self._step - i, self._color) @@ -124,40 +129,42 @@ def step(self, amt = 1): if overflow >= 0: self._step = overflow + class ColorFade(BaseAnimation): """Fill the dots progressively along the strip.""" - def __init__(self, led, colors, step = 0.1, start=0, end=0): - super(ColorFade, self).__init__(led, start, end) + def __init__(self, led_strip, colors, step=0.1, start=0, end=0): + super(ColorFade, self).__init__(led_strip, start, end) self._colors = colors self._levels = util.wave_range(0.4, 1.0, step) self._level_count = len(self._levels) self._color_count = len(colors) - def step(self, amt = 1): + def step(self, amt=1): if self._step > self._level_count * self._color_count: self._step = 0 c_index = (self._step / self._level_count) % self._color_count l_index = (self._step % self._level_count) - color = self._colors[c_index]; + color = self._colors[c_index] self._led.fill(Color(color.r, color.g, color.b, self._levels[l_index]), self._start, self._end) self._step += amt + class ColorChase(BaseAnimation): """Chase one pixel down the strip.""" - def __init__(self, led, color, width=1, start=0, end=0): - super(ColorChase, self).__init__(led, start, end) + def __init__(self, led_strip, color, width=1, start=0, end=0): + super(ColorChase, self).__init__(led_strip, start, end) self._color = color self._width = width - def step(self, amt = 1): + def step(self, amt=1): if self._step == 0: - self._led.setOff(self._end) + self._led.set_off(self._end) else: - self._led.fillOff() #because I am lazy + self._led.fill_off() # because I am lazy for i in range(self._width): self._led.set(self._start + self._step + i, self._color) @@ -167,42 +174,44 @@ def step(self, amt = 1): if overflow >= 0: self._step = overflow + class PartyMode(BaseAnimation): """Stobe Light Effect.""" - def __init__(self, led, colors, start=0, end=0): - super(PartyMode, self).__init__(led, start, end) + def __init__(self, led_strip, colors, start=0, end=0): + super(PartyMode, self).__init__(led_strip, start, end) self._colors = colors self._color_count = len(colors) - def step(self, amt = 1): - amt = 1 #anything other than 1 would be just plain silly + def step(self, amt=1): + amt = 1 # anything other than 1 would be just plain silly if self._step > (self._color_count * 2) - 1: self._step = 0 if self._step % 2 == 0: self._led.fill(self._colors[self._step / 2], self._start, self._end) else: - self._led.fillOff() + self._led.fill_off() self._step += amt + class FireFlies(BaseAnimation): """Stobe Light Effect.""" - def __init__(self, led, colors, width = 1, count = 1, start=0, end=0): - super(FireFlies, self).__init__(led, start, end) + def __init__(self, led_strip, colors, width=1, count=1, start=0, end=0): + super(FireFlies, self).__init__(led_strip, start, end) self._colors = colors self._color_count = len(colors) self._width = width self._count = count - def step(self, amt = 1): - amt = 1 #anything other than 1 would be just plain silly + def step(self, amt=1): + amt = 1 # anything other than 1 would be just plain silly if self._step > self._led.leds: self._step = 0 - self._led.fillOff(); + self._led.fill_off() for i in range(self._count): pixel = random.randint(0, self._led.leds - 1) @@ -214,11 +223,12 @@ def step(self, amt = 1): self._step += amt + class LarsonScanner(BaseAnimation): """Larson scanner (i.e. Cylon Eye or K.I.T.T.).""" - def __init__(self, led, color, tail=2, fade=0.75, start=0, end=0): - super(LarsonScanner, self).__init__(led, start, end) + def __init__(self, led_strip, color, tail=2, fade=0.75, start=0, end=0): + super(LarsonScanner, self).__init__(led_strip, start, end) self._color = color self._tail = tail + 1 # makes tail math later easier @@ -229,7 +239,7 @@ def __init__(self, led, color, tail=2, fade=0.75, start=0, end=0): self._direction = -1 self._last = 0 - def step(self, amt = 1): + def step(self, amt=1): self._last = self._start + self._step self._led.set(self._last, self._color) @@ -240,28 +250,28 @@ def step(self, amt = 1): if self._last - tr < self._start: tr = self._last - self._start - #clear the whole thing - self._led.fillOff(self._start, self._end) + # clear the whole thing + self._led.fill_off(self._start, self._end) for l in range(1, tl + 1): level = (float(self._tail - l) / float(self._tail)) * self._fade - self._led.setRGB(self._last + l, - self._color.r * level, - self._color.g * level, - self._color.b * level) + self._led.set_rgb(self._last + l, + self._color.r * level, + self._color.g * level, + self._color.b * level) if self._last + tl + 1 <= self._end: - self._led.setOff(self._last + tl + 1) + self._led.set_off(self._last + tl + 1) for r in range(1, tr + 1): level = (float(self._tail - r) / float(self._tail)) * self._fade - self._led.setRGB(self._last - r, - self._color.r * level, - self._color.g * level, - self._color.b * level) + self._led.set_rgb(self._last - r, + self._color.r * level, + self._color.g * level, + self._color.b * level) if self._last - tr - 1 >= self._start: - self._led.setOff(self._last - tr - 1) + self._led.set_off(self._last - tr - 1) if self._start + self._step >= self._end: self._direction = -self._direction @@ -270,27 +280,29 @@ def step(self, amt = 1): self._step += self._direction * amt + class LarsonRainbow(LarsonScanner): """Larson scanner (i.e. Cylon Eye or K.I.T.T.) but Rainbow.""" - def __init__(self, led, tail=2, fade=0.75, start=0, end=0): + def __init__(self, led_strip, tail=2, fade=0.75, start=0, end=0): super(LarsonRainbow, self).__init__( - led, ColorHSV(0).get_color_rgb(), tail, fade, start, end) + led_strip, ColorHSV(0).get_color_rgb(), tail, fade, start, end) - def step(self, amt = 1): + def step(self, amt=1): self._color = ColorHSV(self._step * (360 / self._size)).get_color_rgb() super(LarsonRainbow, self).step(amt) + class Wave(BaseAnimation): """Sine wave animation.""" - def __init__(self, led, color, cycles, start=0, end=0): - super(Wave, self).__init__(led, start, end) + def __init__(self, led_strip, color, cycles, start=0, end=0): + super(Wave, self).__init__(led_strip, start, end) self._color = color self._cycles = cycles - def step(self, amt = 1): + def step(self, amt=1): for i in range(self._size): y = math.sin( math.pi * @@ -317,35 +329,92 @@ def step(self, amt = 1): import time import timecolors + + class RGBClock(BaseAnimation): """RGB Clock done with RGB LED strip(s)""" - def __init__(self, led, hStart, hEnd, mStart, mEnd, sStart, sEnd): - super(RGBClock, self).__init__(led, 0, 0) - if hEnd < hStart: - hEnd = hStart + 1 - if mEnd < mStart: - mEnd = mStart + 1 - if sEnd < sStart: - sEnd = sStart + 1 - self._hStart = hStart - self._hEnd = hEnd - self._mStart = mStart - self._mEnd = mEnd - self._sStart = sStart - self._sEnd = sEnd - - - def step(self, amt = 1): + def __init__(self, led_strip, h_start, h_end, m_start, m_end, s_start, s_end): + super(RGBClock, self).__init__(led_strip, 0, 0) + if h_end < h_start: + h_end = h_start + 1 + if m_end < m_start: + m_end = m_start + 1 + if s_end < s_start: + s_end = s_start + 1 + self._hStart = h_start + self._hEnd = h_end + self._mStart = m_start + self._mEnd = m_end + self._sStart = s_start + self._sEnd = s_end + + def step(self, amt=1): t = time.localtime() r, g, b = timecolors._hourColors[t.tm_hour] - self._led.fillRGB(r,g,b,self._hStart,self._hEnd) + self._led.fill_rgb(r, g, b, self._hStart, self._hEnd) r, g, b = timecolors._minSecColors[t.tm_min] - self._led.fillRGB(r,g,b,self._mStart,self._mEnd) + self._led.fill_rgb(r, g, b, self._mStart, self._mEnd) r, g, b = timecolors._minSecColors[t.tm_sec] - self._led.fillRGB(r,g,b,self._sStart,self._sEnd) + self._led.fill_rgb(r, g, b, self._sStart, self._sEnd) + + self._step = 0 + + +class AlertStrobe(BaseAnimation): + def __init__(self, led_strip, strobe_color, start=0, end=0): + self._color = strobe_color + super(AlertStrobe, self).__init__(led_strip, start, end) + + def step(self, step_amount=1): + self._led.fill(self._color) if self._step % 2 else self._led.all_off() + self._step += step_amount + + +class FillFromCenter(BaseAnimation): + def __init__(self, led_strip, fill_color, start=0, end=0): + self._strip_length = led_strip.last_index + self._center_point = int(self._strip_length / 2) + self._color = fill_color + super(FillFromCenter, self).__init__(led_strip, start, end) + + def step(self, step_amount=1): + if self._step > self._center_point: + self._step = 0 + self._led.fill_off() + self._led.set(self._center_point + self._step, self._color) + self._led.set(self._center_point - self._step, self._color) + self._step += 1 + + +class BreathingLight(BaseAnimation): + def __init__(self, led_strip, red, green, blue, min_brightness, max_brightness, start=0, end=0): + super(BreathingLight, self).__init__(led_strip, start, end) + self._base_red = red + self._base_green = green + self._base_blue = blue + self._min_bright = float(min_brightness) + self._max_bright = float(max_brightness) + self._direction = 1 + self._step = 1 + + def scale_brightness(self, step_number): + return self._min_bright + ((self._max_bright - self._min_bright) * (float(step_number) / 255)) + + def step(self, step_amount=1): + if self._step > 254 or self._step < 0: + self._direction *= -1 + + self._led.fill( + Color( + self._base_red, + self._base_green, + self._base_blue, + self.scale_brightness(self._step) + ) + ) + self._step += (1 * self._direction) - self._step = 0 \ No newline at end of file diff --git a/raspledstrip/color.py b/raspledstrip/color.py index d181111..871e9f0 100644 --- a/raspledstrip/color.py +++ b/raspledstrip/color.py @@ -1,16 +1,19 @@ import colorsys + class Color: - """Main color object used by all methods.""" + """ + Main color object used by all methods. + """ def __init__(self, r=0.0, g=0.0, b=0.0, bright=1.0): - """Initialize Color object with optional RGB and brightness values.""" - + """ + Initialize Color object with optional RGB and brightness values. + """ if r > 255.0 or r < 0.0 or g > 255.0 or g < 0.0 or b > 255.0 or b < 0.0: raise ValueError('RGB values must be between 0 and 255') if bright > 1.0 or bright < 0.0: raise ValueError('Brightness must be between 0.0 and 1.0') - self.r = r * bright self.g = g * bright self.b = b * bright @@ -23,14 +26,17 @@ def get_color_hsv(self): def __str__(self): return "%d,%d,%d" % (self.r, self.g, self.b) - -def color_hex(hex): - """Helper for converting RGB and RGBA hex values to Color""" - hex = hex.strip('#') - if len(hex) == 6: - split = (hex[0:2],hex[2:4],hex[4:6]) - elif len(hex) == 8: - split = (hex[0:2],hex[2:4],hex[4:6], hex[6:8]) + + +def color_hex(hex_string): + """ + Helper for converting RGB and RGBA hex values to Color + """ + hex_string = hex_string.strip('#') + if len(hex_string) == 6: + split = (hex_string[0:2], hex_string[2:4], hex_string[4:6]) + elif len(hex_string) == 8: + split = (hex_string[0:2], hex_string[2:4], hex_string[4:6], hex_string[6:8]) else: raise ValueError('Must pass in either a 6 or 8 character hex value!') @@ -40,12 +46,14 @@ def color_hex(hex): else: r, g, b, alpha = [int(x, 16) for x in split] - alpha = alpha / 255.0 + alpha /= 255.0 return Color(r, g, b, alpha) + class ColorHSV: - """Useful for natural color transitions. + """ + Useful for natural color transitions. Increment hue to sweep through the colors. Must call getColorRGB() before passing to any of the methods. @@ -72,7 +80,9 @@ def __str__(self): def wheel_color(position): - """Get color from wheel value (0 - 384).""" + """ + Get color from wheel value (0 - 384). + """ if position < 0: position = 0 if position > 384: @@ -99,9 +109,7 @@ class SysColors: white75 = Color(255, 255, 255, 0.75) white50 = Color(255, 255, 255, 0.5) white25 = Color(255, 255, 255, 0.25) - - off = Color(0, 0, 0); - + off = Color(0, 0, 0) red = Color(255, 0, 0) orange = Color(255, 127, 0) yellow = Color(255, 255, 0) diff --git a/raspledstrip/ledstrip.py b/raspledstrip/ledstrip.py index 1561f57..2b570eb 100644 --- a/raspledstrip/ledstrip.py +++ b/raspledstrip/ledstrip.py @@ -1,118 +1,192 @@ #!/usr/bin/env python from color import Color, ColorHSV -from LPD8806 import LPD8806 -#Not all LPD8806 strands are created equal. -#Some, like Adafruit's use GRB order and the other common order is GRB -#Library defaults to GRB but you can call strand.setChannelOrder(ChannelOrder) -#to set the order your strands use + class ChannelOrder: - RGB = [0,1,2] #Probably not used, here for clarity - - GRB = [1,0,2] #Strands from Adafruit and some others (default) - BRG = [1,2,0] #Strands from many other manufacturers + """ + Not all LPD8806 strands are created equal. + Some, like Adafruit's use GRB order and the other common order is GRB + Library defaults to GRB but you can call strand.setChannelOrder(ChannelOrder) + to set the order your strands use + """ + + RGB = [0, 1, 2] # Probably not used, here for clarity + GRB = [1, 0, 2] # Strands from Adafruit and some others (default) + BRG = [1, 2, 0] # Strands from many other manufacturers class LEDStrip: - def __init__(self, leds, use_py_spi = False, dev="/dev/spidev0.0", driver="LPD8806"): - #Variables: - # leds -- strand size - # dev -- spi device - - #no alternate drivers for now. Here so they can be added later - self.driver = LPD8806(leds, use_py_spi, dev) + def __init__(self, driver): + """ + :param driver: :class:`LPD8806.LEDDriver` + :return: + """ + + self.driver = driver + self.led_count = driver.get_led_count() self.c_order = ChannelOrder.GRB - self.leds = leds - self.lastIndex = self.leds - 1 - self.gamma = bytearray(256) - self.buffer = [0 for x in range(self.leds + 1)] - - self.masterBrightness = 1.0 + self.last_index = self.led_count - 1 + self.master_brightness = 1.0 + self.pixel_buffer = [bytearray(3) for i in range(self.led_count)] - for led in range(self.leds): - self.buffer[led] = bytearray(3) - for i in range(256): - # Color calculations from - # http://learn.adafruit.com/light-painting-with-raspberry-pi - self.gamma[i] = 0x80 | int( - pow(float(i) / 255.0, 2.5) * 127.0 + 0.5 - ) + # Color calculations from + # http://learn.adafruit.com/light-painting-with-raspberry-pi + self.gamma = [0x80 | int(pow(float(i) / 255.0, 2.5) * 127.0 + 0.5) for i in range(256)] def update(self): - self.driver.update(self.buffer) - - #Allows for easily using LED strands with different channel orders - def setChannelOrder(self, order): + self.driver.update(self.pixel_buffer) + + def set_channel_order(self, order): + """ + Allows for easily using LED strands with different channel orders + :param order: :class:`ChannelOrder` + :return: + """ self.c_order = order - #Set the master brightness for the LEDs 0.0 - 1.0 - def setMasterBrightness(self, bright): - if(bright > 1.0 or bright < 0.0): + def set_master_brightness(self, bright): + """ + Set the master brightness for the LEDs 0.0 - 1.0 + :param bright: + :return: + """ + if bright > 1.0 or bright < 0.0: raise ValueError('Brightness must be between 0.0 and 1.0') - self.masterBrightness = bright + self.master_brightness = bright - #Fill the strand (or a subset) with a single color using a Color object def fill(self, color, start=0, end=0): + """ + Fill the strand (or a subset) with a single color using a Color object + :param color: + :param start: + :param end: + :return: + """ if start < 0: start = 0 - if end == 0 or end > self.lastIndex: - end = self.lastIndex - for led in range(start, end + 1): #since 0-index include end in range + if end == 0 or end > self.last_index: + end = self.last_index + for led in range(start, end + 1): # since 0-index include end in range self.__set_internal(led, color) - #Fill the strand (or a subset) with a single color using RGB values - def fillRGB(self, r, g, b, start=0, end=0): + def fill_rgb(self, r, g, b, start=0, end=0): + """ + Fill the strand (or a subset) with a single color using RGB values + :param r: + :param g: + :param b: + :param start: + :param end: + :return: + """ self.fill(Color(r, g, b), start, end) - #Fill the strand (or a subset) with a single color using HSV values - def fillHSV(self, h, s, v, start=0, end=0): + def fill_hsv(self, h, s, v, start=0, end=0): + """ + Fill the strand (or a subset) with a single color using HSV values + :param h: + :param s: + :param v: + :param start: + :param end: + :return: + """ self.fill(ColorHSV(h, s, v).get_color_rgb(), start, end) - #Fill the strand (or a subset) with a single color using a Hue value. - #Saturation and Value components of HSV are set to max. - def fillHue(self, hue, start=0, end=0): + def fill_hue(self, hue, start=0, end=0): + """ + #Fill the strand (or a subset) with a single color using a Hue value. + #Saturation and Value components of HSV are set to max. + :param hue: + :param start: + :param end: + :return: + """ + self.fill(ColorHSV(hue).get_color_rgb(), start, end) - def fillOff(self, start=0, end=0): - self.fillRGB(0, 0, 0, start, end) + def fill_off(self, start=0, end=0): + """ + Turn off the entire strand (or a subset) + :param start: + :param end: + :return: + """ + self.fill_rgb(0, 0, 0, start, end) - #internal use only. sets pixel color def __set_internal(self, pixel, color): - if(pixel < 0 or pixel > self.lastIndex): - return; #don't go out of bounds - - self.buffer[pixel][self.c_order[0]] = self.gamma[int(color.r * self.masterBrightness)] - self.buffer[pixel][self.c_order[1]] = self.gamma[int(color.g * self.masterBrightness)] - self.buffer[pixel][self.c_order[2]] = self.gamma[int(color.b * self.masterBrightness)] + """ + internal use only. sets pixel color + :param pixel: + :param color: + :return: + """ + if pixel < 0 or pixel > self.last_index: + return # don't go out of bounds + + self.pixel_buffer[pixel][self.c_order[0]] = self.gamma[int(color.r * self.master_brightness)] + self.pixel_buffer[pixel][self.c_order[1]] = self.gamma[int(color.g * self.master_brightness)] + self.pixel_buffer[pixel][self.c_order[2]] = self.gamma[int(color.b * self.master_brightness)] - #Set single pixel to Color value def set(self, pixel, color): + """ + Set single pixel to Color value + :param pixel: + :param color: + :return: + """ self.__set_internal(pixel, color) - #Set single pixel to RGB value - def setRGB(self, pixel, r, g, b): + def set_rgb(self, pixel, r, g, b): + """ + Set single pixel to RGB value + :param pixel: + :param r: + :param g: + :param b: + :return: + """ color = Color(r, g, b) self.set(pixel, color) - #Set single pixel to HSV value - def setHSV(self, pixel, h, s, v): + def set_hsv(self, pixel, h, s, v): + """ + Set single pixel to HSV value + :param pixel: + :param h: + :param s: + :param v: + :return: + """ self.set(pixel, ColorHSV(h, s, v).get_color_rgb()) - #Set single pixel to Hue value. - #Saturation and Value components of HSV are set to max. - def setHue(self, pixel, hue): + def set_hue(self, pixel, hue): + """ + Set single pixel to Hue value. + Saturation and Value components of HSV are set to max. + :param pixel: + :param hue: + :return: + """ self.set(pixel, ColorHSV(hue).get_color_rgb()) - #turns off the desired pixel - def setOff(self, pixel): - self.setRGB(pixel, 0, 0, 0) + def set_off(self, pixel): + """ + turns off the desired pixel + :param pixel: + :return: + """ + self.set_rgb(pixel, 0, 0, 0) - #Turn all LEDs off. def all_off(self): - self.fillOff() + """ + Turn all LEDs off. + :return: + """ + self.fill_off() self.update() - self.fillOff() + self.fill_off() self.update() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..57ab588 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +mock==1.0.1 +nose==1.3.3 +PyAudio==0.2.8 +numpy==1.8.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 81e7fe8..8bcccdd 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='RasPiLEDStrip', - version='0.9b', + version='0.10', description='Driver and Animation classes for driving LED Strips from the Raspberry Pi', author='Adam Haile', author_email='adam@maniacallabs.com', diff --git a/test_animation.py b/test_animation.py new file mode 100644 index 0000000..6e2e9cb --- /dev/null +++ b/test_animation.py @@ -0,0 +1,18 @@ +from raspledstrip.ledstrip import LEDStrip +from raspledstrip.LPD8806 import LPD8806SPI +from raspledstrip.animation import AlertStrobe, FillFromCenter, BreathingLight +from raspledstrip.color import Color + +led_strip = LEDStrip(LPD8806SPI(36)) +led_strip.all_off() + +alert_color = Color(255, 0, 0, 1.0) +fill_animation = FillFromCenter(led_strip, alert_color) +fill_animation.run(1, 30, 18) +alert_animation = AlertStrobe(led_strip, alert_color) +alert_animation.run(1, 20, 24) +led_strip.all_off() +fill_animation = BreathingLight(led_strip, 0, 255, 0, 0.1, 1.0, 0, 0) +fill_animation.run(1, 30, 1000) +led_strip.all_off() + diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/animation_tests.py b/tests/animation_tests.py new file mode 100644 index 0000000..6fe0eaf --- /dev/null +++ b/tests/animation_tests.py @@ -0,0 +1,118 @@ +import unittest +import mock +import raspledstrip.animation + + +class BreathingLightTests(unittest.TestCase): + def test_scale_brightness_full_range(self): + mock_driver = mock.Mock() + mock_driver.last_index = 35 + animation = raspledstrip.animation.BreathingLight( + mock_driver, + 255, + 255, + 255, + 0, + 1, + 0, + 0 + ) + + step_intensity = [] + for i in xrange(255): + intensity = animation.scale_brightness(float(i)) + step_intensity.append(intensity) + self.assertListEqual( + step_intensity, + [0.0, 0.00392156862745098, 0.00784313725490196, 0.011764705882352941, 0.01568627450980392, 0.0196078431372549, 0.023529411764705882, 0.027450980392156862, 0.03137254901960784, 0.03529411764705882, 0.0392156862745098, 0.043137254901960784, 0.047058823529411764, 0.050980392156862744, 0.054901960784313725, 0.058823529411764705, 0.06274509803921569, 0.06666666666666667, 0.07058823529411765, 0.07450980392156863, 0.0784313725490196, 0.08235294117647059, 0.08627450980392157, 0.09019607843137255, 0.09411764705882353, 0.09803921568627451, 0.10196078431372549, 0.10588235294117647, 0.10980392156862745, 0.11372549019607843, 0.11764705882352941, 0.12156862745098039, 0.12549019607843137, 0.12941176470588237, 0.13333333333333333, 0.13725490196078433, 0.1411764705882353, 0.1450980392156863, 0.14901960784313725, 0.15294117647058825, 0.1568627450980392, 0.1607843137254902, 0.16470588235294117, 0.16862745098039217, 0.17254901960784313, 0.17647058823529413, 0.1803921568627451, 0.1843137254901961, 0.18823529411764706, 0.19215686274509805, 0.19607843137254902, 0.2, 0.20392156862745098, 0.20784313725490197, 0.21176470588235294, 0.21568627450980393, 0.2196078431372549, 0.2235294117647059, 0.22745098039215686, 0.23137254901960785, 0.23529411764705882, 0.23921568627450981, 0.24313725490196078, 0.24705882352941178, 0.25098039215686274, 0.2549019607843137, 0.25882352941176473, 0.2627450980392157, 0.26666666666666666, 0.27058823529411763, 0.27450980392156865, 0.2784313725490196, 0.2823529411764706, 0.28627450980392155, 0.2901960784313726, 0.29411764705882354, 0.2980392156862745, 0.30196078431372547, 0.3058823529411765, 0.30980392156862746, 0.3137254901960784, 0.3176470588235294, 0.3215686274509804, 0.3254901960784314, 0.32941176470588235, 0.3333333333333333, 0.33725490196078434, 0.3411764705882353, 0.34509803921568627, 0.34901960784313724, 0.35294117647058826, 0.3568627450980392, 0.3607843137254902, 0.36470588235294116, 0.3686274509803922, 0.37254901960784315, 0.3764705882352941, 0.3803921568627451, 0.3843137254901961, 0.38823529411764707, 0.39215686274509803, 0.396078431372549, 0.4, 0.403921568627451, 0.40784313725490196, 0.4117647058823529, 0.41568627450980394, 0.4196078431372549, 0.4235294117647059, 0.42745098039215684, 0.43137254901960786, 0.43529411764705883, 0.4392156862745098, 0.44313725490196076, 0.4470588235294118, 0.45098039215686275, 0.4549019607843137, 0.4588235294117647, 0.4627450980392157, 0.4666666666666667, 0.47058823529411764, 0.4745098039215686, 0.47843137254901963, 0.4823529411764706, 0.48627450980392156, 0.49019607843137253, 0.49411764705882355, 0.4980392156862745, 0.5019607843137255, 0.5058823529411764, 0.5098039215686274, 0.5137254901960784, 0.5176470588235295, 0.5215686274509804, 0.5254901960784314, 0.5294117647058824, 0.5333333333333333, 0.5372549019607843, 0.5411764705882353, 0.5450980392156862, 0.5490196078431373, 0.5529411764705883, 0.5568627450980392, 0.5607843137254902, 0.5647058823529412, 0.5686274509803921, 0.5725490196078431, 0.5764705882352941, 0.5803921568627451, 0.5843137254901961, 0.5882352941176471, 0.592156862745098, 0.596078431372549, 0.6, 0.6039215686274509, 0.6078431372549019, 0.611764705882353, 0.615686274509804, 0.6196078431372549, 0.6235294117647059, 0.6274509803921569, 0.6313725490196078, 0.6352941176470588, 0.6392156862745098, 0.6431372549019608, 0.6470588235294118, 0.6509803921568628, 0.6549019607843137, 0.6588235294117647, 0.6627450980392157, 0.6666666666666666, 0.6705882352941176, 0.6745098039215687, 0.6784313725490196, 0.6823529411764706, 0.6862745098039216, 0.6901960784313725, 0.6941176470588235, 0.6980392156862745, 0.7019607843137254, 0.7058823529411765, 0.7098039215686275, 0.7137254901960784, 0.7176470588235294, 0.7215686274509804, 0.7254901960784313, 0.7294117647058823, 0.7333333333333333, 0.7372549019607844, 0.7411764705882353, 0.7450980392156863, 0.7490196078431373, 0.7529411764705882, 0.7568627450980392, 0.7607843137254902, 0.7647058823529411, 0.7686274509803922, 0.7725490196078432, 0.7764705882352941, 0.7803921568627451, 0.7843137254901961, 0.788235294117647, 0.792156862745098, 0.796078431372549, 0.8, 0.803921568627451, 0.807843137254902, 0.8117647058823529, 0.8156862745098039, 0.8196078431372549, 0.8235294117647058, 0.8274509803921568, 0.8313725490196079, 0.8352941176470589, 0.8392156862745098, 0.8431372549019608, 0.8470588235294118, 0.8509803921568627, 0.8549019607843137, 0.8588235294117647, 0.8627450980392157, 0.8666666666666667, 0.8705882352941177, 0.8745098039215686, 0.8784313725490196, 0.8823529411764706, 0.8862745098039215, 0.8901960784313725, 0.8941176470588236, 0.8980392156862745, 0.9019607843137255, 0.9058823529411765, 0.9098039215686274, 0.9137254901960784, 0.9176470588235294, 0.9215686274509803, 0.9254901960784314, 0.9294117647058824, 0.9333333333333333, 0.9372549019607843, 0.9411764705882353, 0.9450980392156862, 0.9490196078431372, 0.9529411764705882, 0.9568627450980393, 0.9607843137254902, 0.9647058823529412, 0.9686274509803922, 0.9725490196078431, 0.9764705882352941, 0.9803921568627451, 0.984313725490196, 0.9882352941176471, 0.9921568627450981, 0.996078431372549] + ) + self.assertEqual(255, len(step_intensity)) + + def test_scale_brightness_with_min(self): + mock_driver = mock.Mock() + mock_driver.last_index = 35 + animation = raspledstrip.animation.BreathingLight( + mock_driver, + 255, + 255, + 255, + .5, + 1, + 0, + 0 + ) + + step_intensity = [] + for i in xrange(255): + intensity = animation.scale_brightness(float(i)) + step_intensity.append(intensity) + self.assertListEqual( + step_intensity, + [0.5, 0.5019607843137255, 0.503921568627451, 0.5058823529411764, 0.5078431372549019, 0.5098039215686274, 0.5117647058823529, 0.5137254901960784, 0.5156862745098039, 0.5176470588235295, 0.5196078431372549, 0.5215686274509804, 0.5235294117647059, 0.5254901960784314, 0.5274509803921569, 0.5294117647058824, 0.5313725490196078, 0.5333333333333333, 0.5352941176470588, 0.5372549019607843, 0.5392156862745098, 0.5411764705882353, 0.5431372549019607, 0.5450980392156862, 0.5470588235294118, 0.5490196078431373, 0.5509803921568628, 0.5529411764705883, 0.5549019607843138, 0.5568627450980392, 0.5588235294117647, 0.5607843137254902, 0.5627450980392157, 0.5647058823529412, 0.5666666666666667, 0.5686274509803921, 0.5705882352941176, 0.5725490196078431, 0.5745098039215686, 0.5764705882352941, 0.5784313725490196, 0.5803921568627451, 0.5823529411764706, 0.5843137254901961, 0.5862745098039216, 0.5882352941176471, 0.5901960784313726, 0.592156862745098, 0.5941176470588235, 0.596078431372549, 0.5980392156862745, 0.6, 0.6019607843137255, 0.6039215686274509, 0.6058823529411764, 0.607843137254902, 0.6098039215686275, 0.611764705882353, 0.6137254901960785, 0.615686274509804, 0.6176470588235294, 0.6196078431372549, 0.6215686274509804, 0.6235294117647059, 0.6254901960784314, 0.6274509803921569, 0.6294117647058823, 0.6313725490196078, 0.6333333333333333, 0.6352941176470588, 0.6372549019607843, 0.6392156862745098, 0.6411764705882352, 0.6431372549019607, 0.6450980392156863, 0.6470588235294118, 0.6490196078431373, 0.6509803921568628, 0.6529411764705882, 0.6549019607843137, 0.6568627450980392, 0.6588235294117647, 0.6607843137254902, 0.6627450980392157, 0.6647058823529411, 0.6666666666666666, 0.6686274509803922, 0.6705882352941177, 0.6725490196078432, 0.6745098039215687, 0.6764705882352942, 0.6784313725490196, 0.6803921568627451, 0.6823529411764706, 0.6843137254901961, 0.6862745098039216, 0.6882352941176471, 0.6901960784313725, 0.692156862745098, 0.6941176470588235, 0.696078431372549, 0.6980392156862745, 0.7, 0.7019607843137254, 0.7039215686274509, 0.7058823529411764, 0.707843137254902, 0.7098039215686275, 0.711764705882353, 0.7137254901960784, 0.7156862745098039, 0.7176470588235294, 0.7196078431372549, 0.7215686274509804, 0.7235294117647059, 0.7254901960784313, 0.7274509803921568, 0.7294117647058823, 0.7313725490196079, 0.7333333333333334, 0.7352941176470589, 0.7372549019607844, 0.7392156862745098, 0.7411764705882353, 0.7431372549019608, 0.7450980392156863, 0.7470588235294118, 0.7490196078431373, 0.7509803921568627, 0.7529411764705882, 0.7549019607843137, 0.7568627450980392, 0.7588235294117647, 0.7607843137254902, 0.7627450980392156, 0.7647058823529411, 0.7666666666666666, 0.7686274509803921, 0.7705882352941176, 0.7725490196078431, 0.7745098039215687, 0.7764705882352941, 0.7784313725490196, 0.7803921568627451, 0.7823529411764706, 0.7843137254901961, 0.7862745098039216, 0.788235294117647, 0.7901960784313726, 0.7921568627450981, 0.7941176470588236, 0.7960784313725491, 0.7980392156862746, 0.8, 0.8019607843137255, 0.803921568627451, 0.8058823529411765, 0.807843137254902, 0.8098039215686275, 0.8117647058823529, 0.8137254901960784, 0.8156862745098039, 0.8176470588235294, 0.8196078431372549, 0.8215686274509804, 0.8235294117647058, 0.8254901960784313, 0.8274509803921568, 0.8294117647058823, 0.8313725490196078, 0.8333333333333333, 0.8352941176470587, 0.8372549019607843, 0.8392156862745098, 0.8411764705882353, 0.8431372549019608, 0.8450980392156863, 0.8470588235294118, 0.8490196078431372, 0.8509803921568627, 0.8529411764705883, 0.8549019607843138, 0.8568627450980393, 0.8588235294117648, 0.8607843137254902, 0.8627450980392157, 0.8647058823529412, 0.8666666666666667, 0.8686274509803922, 0.8705882352941177, 0.8725490196078431, 0.8745098039215686, 0.8764705882352941, 0.8784313725490196, 0.8803921568627451, 0.8823529411764706, 0.884313725490196, 0.8862745098039215, 0.888235294117647, 0.8901960784313725, 0.892156862745098, 0.8941176470588235, 0.896078431372549, 0.8980392156862744, 0.9, 0.9019607843137255, 0.903921568627451, 0.9058823529411765, 0.907843137254902, 0.9098039215686274, 0.9117647058823529, 0.9137254901960784, 0.915686274509804, 0.9176470588235295, 0.919607843137255, 0.9215686274509804, 0.9235294117647059, 0.9254901960784314, 0.9274509803921569, 0.9294117647058824, 0.9313725490196079, 0.9333333333333333, 0.9352941176470588, 0.9372549019607843, 0.9392156862745098, 0.9411764705882353, 0.9431372549019608, 0.9450980392156862, 0.9470588235294117, 0.9490196078431372, 0.9509803921568627, 0.9529411764705882, 0.9549019607843137, 0.9568627450980391, 0.9588235294117646, 0.9607843137254901, 0.9627450980392157, 0.9647058823529412, 0.9666666666666667, 0.9686274509803922, 0.9705882352941176, 0.9725490196078431, 0.9745098039215686, 0.9764705882352941, 0.9784313725490197, 0.9803921568627452, 0.9823529411764707, 0.9843137254901961, 0.9862745098039216, 0.9882352941176471, 0.9901960784313726, 0.9921568627450981, 0.9941176470588236, 0.996078431372549, 0.9980392156862745] + ) + self.assertEqual(255, len(step_intensity)) + + def test_scale_brightness_with_max(self): + mock_driver = mock.Mock() + mock_driver.last_index = 35 + animation = raspledstrip.animation.BreathingLight( + mock_driver, + 255, + 255, + 255, + 0, + .5, + 0, + 0 + ) + + step_intensity = [] + for i in xrange(255): + intensity = animation.scale_brightness(float(i)) + step_intensity.append(intensity) + self.assertListEqual( + step_intensity, + [0.0, 0.00196078431372549, 0.00392156862745098, 0.0058823529411764705, 0.00784313725490196, 0.00980392156862745, 0.011764705882352941, 0.013725490196078431, 0.01568627450980392, 0.01764705882352941, 0.0196078431372549, 0.021568627450980392, 0.023529411764705882, 0.025490196078431372, 0.027450980392156862, 0.029411764705882353, 0.03137254901960784, 0.03333333333333333, 0.03529411764705882, 0.03725490196078431, 0.0392156862745098, 0.041176470588235294, 0.043137254901960784, 0.045098039215686274, 0.047058823529411764, 0.049019607843137254, 0.050980392156862744, 0.052941176470588235, 0.054901960784313725, 0.056862745098039215, 0.058823529411764705, 0.060784313725490195, 0.06274509803921569, 0.06470588235294118, 0.06666666666666667, 0.06862745098039216, 0.07058823529411765, 0.07254901960784314, 0.07450980392156863, 0.07647058823529412, 0.0784313725490196, 0.0803921568627451, 0.08235294117647059, 0.08431372549019608, 0.08627450980392157, 0.08823529411764706, 0.09019607843137255, 0.09215686274509804, 0.09411764705882353, 0.09607843137254903, 0.09803921568627451, 0.1, 0.10196078431372549, 0.10392156862745099, 0.10588235294117647, 0.10784313725490197, 0.10980392156862745, 0.11176470588235295, 0.11372549019607843, 0.11568627450980393, 0.11764705882352941, 0.11960784313725491, 0.12156862745098039, 0.12352941176470589, 0.12549019607843137, 0.12745098039215685, 0.12941176470588237, 0.13137254901960785, 0.13333333333333333, 0.13529411764705881, 0.13725490196078433, 0.1392156862745098, 0.1411764705882353, 0.14313725490196078, 0.1450980392156863, 0.14705882352941177, 0.14901960784313725, 0.15098039215686274, 0.15294117647058825, 0.15490196078431373, 0.1568627450980392, 0.1588235294117647, 0.1607843137254902, 0.1627450980392157, 0.16470588235294117, 0.16666666666666666, 0.16862745098039217, 0.17058823529411765, 0.17254901960784313, 0.17450980392156862, 0.17647058823529413, 0.1784313725490196, 0.1803921568627451, 0.18235294117647058, 0.1843137254901961, 0.18627450980392157, 0.18823529411764706, 0.19019607843137254, 0.19215686274509805, 0.19411764705882353, 0.19607843137254902, 0.1980392156862745, 0.2, 0.2019607843137255, 0.20392156862745098, 0.20588235294117646, 0.20784313725490197, 0.20980392156862746, 0.21176470588235294, 0.21372549019607842, 0.21568627450980393, 0.21764705882352942, 0.2196078431372549, 0.22156862745098038, 0.2235294117647059, 0.22549019607843138, 0.22745098039215686, 0.22941176470588234, 0.23137254901960785, 0.23333333333333334, 0.23529411764705882, 0.2372549019607843, 0.23921568627450981, 0.2411764705882353, 0.24313725490196078, 0.24509803921568626, 0.24705882352941178, 0.24901960784313726, 0.25098039215686274, 0.2529411764705882, 0.2549019607843137, 0.2568627450980392, 0.25882352941176473, 0.2607843137254902, 0.2627450980392157, 0.2647058823529412, 0.26666666666666666, 0.26862745098039215, 0.27058823529411763, 0.2725490196078431, 0.27450980392156865, 0.27647058823529413, 0.2784313725490196, 0.2803921568627451, 0.2823529411764706, 0.28431372549019607, 0.28627450980392155, 0.28823529411764703, 0.2901960784313726, 0.29215686274509806, 0.29411764705882354, 0.296078431372549, 0.2980392156862745, 0.3, 0.30196078431372547, 0.30392156862745096, 0.3058823529411765, 0.307843137254902, 0.30980392156862746, 0.31176470588235294, 0.3137254901960784, 0.3156862745098039, 0.3176470588235294, 0.3196078431372549, 0.3215686274509804, 0.3235294117647059, 0.3254901960784314, 0.32745098039215687, 0.32941176470588235, 0.33137254901960783, 0.3333333333333333, 0.3352941176470588, 0.33725490196078434, 0.3392156862745098, 0.3411764705882353, 0.3431372549019608, 0.34509803921568627, 0.34705882352941175, 0.34901960784313724, 0.3509803921568627, 0.35294117647058826, 0.35490196078431374, 0.3568627450980392, 0.3588235294117647, 0.3607843137254902, 0.3627450980392157, 0.36470588235294116, 0.36666666666666664, 0.3686274509803922, 0.37058823529411766, 0.37254901960784315, 0.37450980392156863, 0.3764705882352941, 0.3784313725490196, 0.3803921568627451, 0.38235294117647056, 0.3843137254901961, 0.3862745098039216, 0.38823529411764707, 0.39019607843137255, 0.39215686274509803, 0.3941176470588235, 0.396078431372549, 0.3980392156862745, 0.4, 0.4019607843137255, 0.403921568627451, 0.40588235294117647, 0.40784313725490196, 0.40980392156862744, 0.4117647058823529, 0.4137254901960784, 0.41568627450980394, 0.4176470588235294, 0.4196078431372549, 0.4215686274509804, 0.4235294117647059, 0.42549019607843136, 0.42745098039215684, 0.4294117647058823, 0.43137254901960786, 0.43333333333333335, 0.43529411764705883, 0.4372549019607843, 0.4392156862745098, 0.4411764705882353, 0.44313725490196076, 0.44509803921568625, 0.4470588235294118, 0.44901960784313727, 0.45098039215686275, 0.45294117647058824, 0.4549019607843137, 0.4568627450980392, 0.4588235294117647, 0.46078431372549017, 0.4627450980392157, 0.4647058823529412, 0.4666666666666667, 0.46862745098039216, 0.47058823529411764, 0.4725490196078431, 0.4745098039215686, 0.4764705882352941, 0.47843137254901963, 0.4803921568627451, 0.4823529411764706, 0.4843137254901961, 0.48627450980392156, 0.48823529411764705, 0.49019607843137253, 0.492156862745098, 0.49411764705882355, 0.49607843137254903, 0.4980392156862745] + ) + self.assertEqual(255, len(step_intensity)) + + def test_scale_brightness_with_range(self): + mock_driver = mock.Mock() + mock_driver.last_index = 35 + animation = raspledstrip.animation.BreathingLight( + mock_driver, + 255, + 255, + 255, + .2, + .5, + 0, + 0 + ) + step_intensity = [] + for i in xrange(255): + intensity = animation.scale_brightness(float(i)) + step_intensity.append(intensity) + self.assertListEqual( + step_intensity, + [0.2, 0.20117647058823532, 0.2023529411764706, 0.2035294117647059, 0.20470588235294118, 0.2058823529411765, 0.20705882352941177, 0.20823529411764707, 0.20941176470588235, 0.21058823529411766, 0.21176470588235297, 0.21294117647058824, 0.21411764705882355, 0.21529411764705883, 0.21647058823529414, 0.21764705882352942, 0.21882352941176472, 0.22, 0.2211764705882353, 0.2223529411764706, 0.2235294117647059, 0.2247058823529412, 0.22588235294117648, 0.22705882352941179, 0.22823529411764706, 0.22941176470588237, 0.23058823529411765, 0.23176470588235296, 0.23294117647058823, 0.23411764705882354, 0.23529411764705882, 0.23647058823529413, 0.23764705882352943, 0.2388235294117647, 0.24000000000000002, 0.2411764705882353, 0.2423529411764706, 0.24352941176470588, 0.2447058823529412, 0.2458823529411765, 0.24705882352941178, 0.24823529411764708, 0.24941176470588236, 0.25058823529411767, 0.25176470588235295, 0.2529411764705882, 0.25411764705882356, 0.25529411764705884, 0.2564705882352941, 0.25764705882352945, 0.25882352941176473, 0.26, 0.2611764705882353, 0.2623529411764706, 0.2635294117647059, 0.2647058823529412, 0.26588235294117646, 0.2670588235294118, 0.26823529411764707, 0.26941176470588235, 0.2705882352941177, 0.27176470588235296, 0.27294117647058824, 0.2741176470588235, 0.2752941176470588, 0.27647058823529413, 0.2776470588235294, 0.2788235294117647, 0.28, 0.2811764705882353, 0.2823529411764706, 0.2835294117647059, 0.2847058823529412, 0.2858823529411765, 0.2870588235294118, 0.2882352941176471, 0.28941176470588237, 0.29058823529411765, 0.2917647058823529, 0.29294117647058826, 0.29411764705882354, 0.2952941176470588, 0.29647058823529415, 0.29764705882352943, 0.2988235294117647, 0.3, 0.3011764705882353, 0.3023529411764706, 0.3035294117647059, 0.30470588235294116, 0.3058823529411765, 0.3070588235294118, 0.30823529411764705, 0.3094117647058824, 0.31058823529411766, 0.31176470588235294, 0.3129411764705882, 0.3141176470588235, 0.31529411764705884, 0.3164705882352941, 0.3176470588235294, 0.31882352941176473, 0.32, 0.3211764705882353, 0.3223529411764706, 0.3235294117647059, 0.3247058823529412, 0.3258823529411765, 0.32705882352941174, 0.32823529411764707, 0.3294117647058824, 0.3305882352941176, 0.33176470588235296, 0.33294117647058824, 0.3341176470588235, 0.33529411764705885, 0.33647058823529413, 0.3376470588235294, 0.33882352941176475, 0.33999999999999997, 0.3411764705882353, 0.3423529411764706, 0.34352941176470586, 0.3447058823529412, 0.3458823529411765, 0.34705882352941175, 0.3482352941176471, 0.34941176470588237, 0.35058823529411764, 0.351764705882353, 0.3529411764705882, 0.35411764705882354, 0.35529411764705887, 0.3564705882352941, 0.35764705882352943, 0.3588235294117647, 0.36, 0.3611764705882353, 0.36235294117647054, 0.3635294117647059, 0.3647058823529412, 0.3658823529411765, 0.36705882352941177, 0.3682352941176471, 0.36941176470588233, 0.37058823529411766, 0.37176470588235294, 0.3729411764705882, 0.37411764705882355, 0.37529411764705883, 0.3764705882352941, 0.37764705882352945, 0.37882352941176467, 0.38, 0.3811764705882353, 0.38235294117647056, 0.3835294117647059, 0.38470588235294123, 0.38588235294117645, 0.3870588235294118, 0.38823529411764707, 0.38941176470588235, 0.3905882352941177, 0.3917647058823529, 0.39294117647058824, 0.3941176470588236, 0.3952941176470588, 0.39647058823529413, 0.3976470588235294, 0.3988235294117647, 0.4, 0.40117647058823525, 0.4023529411764706, 0.4035294117647059, 0.4047058823529412, 0.40588235294117647, 0.4070588235294118, 0.40823529411764703, 0.40941176470588236, 0.41058823529411764, 0.4117647058823529, 0.41294117647058826, 0.41411764705882353, 0.4152941176470588, 0.41647058823529415, 0.41764705882352937, 0.4188235294117647, 0.42, 0.4211764705882353, 0.4223529411764706, 0.42352941176470593, 0.42470588235294116, 0.4258823529411765, 0.42705882352941177, 0.42823529411764705, 0.4294117647058823, 0.43058823529411766, 0.43176470588235294, 0.4329411764705883, 0.4341176470588235, 0.43529411764705883, 0.4364705882352941, 0.4376470588235294, 0.4388235294117647, 0.44, 0.4411764705882353, 0.4423529411764706, 0.4435294117647059, 0.4447058823529412, 0.44588235294117645, 0.44705882352941173, 0.44823529411764707, 0.4494117647058824, 0.4505882352941177, 0.45176470588235296, 0.45294117647058824, 0.4541176470588235, 0.4552941176470588, 0.45647058823529413, 0.4576470588235294, 0.45882352941176474, 0.46, 0.4611764705882353, 0.4623529411764706, 0.46352941176470586, 0.4647058823529412, 0.46588235294117647, 0.46705882352941175, 0.4682352941176471, 0.46941176470588236, 0.47058823529411764, 0.4717647058823529, 0.47294117647058825, 0.47411764705882353, 0.4752941176470588, 0.4764705882352941, 0.4776470588235294, 0.4788235294117647, 0.48, 0.4811764705882353, 0.4823529411764706, 0.4835294117647059, 0.48470588235294115, 0.48588235294117643, 0.48705882352941177, 0.48823529411764705, 0.4894117647058824, 0.49058823529411766, 0.49176470588235294, 0.4929411764705882, 0.4941176470588235, 0.49529411764705883, 0.4964705882352941, 0.49764705882352944, 0.4988235294117647] + ) + self.assertEqual(255, len(step_intensity)) + + def test_step(self): + mock_driver = mock.Mock() + mock_driver.last_index = 35 + animation = raspledstrip.animation.BreathingLight( + led_strip=mock_driver, + red=0, + green=255, + blue=0, + min_brightness=0.1, + max_brightness=1, + start=0, + end=0 + ) + for i in xrange(512): + animation.step(1) + + self.assertTrue(False) \ No newline at end of file diff --git a/visualize_audio_client.py b/visualize_audio_client.py new file mode 100644 index 0000000..832742d --- /dev/null +++ b/visualize_audio_client.py @@ -0,0 +1,80 @@ +import wave +import pyaudio +import numpy +import socket +import struct + +LED_DRIVER_HOST = '0.0.0.0' #Set this to the address of the raspberry pi with the LEDs attached +#LED_DRIVER_HOST = 'localhost' +LED_DRIVER_PORT = 50007 +BUFFER_SIZE = 512 + + +def main(): + display_buffer = [bytearray([0x00, 0x00, 0x00]) for i in xrange(36)] + + #input_stream = FileReader('8k8bitpcm.wav') # local file for testing + input_stream = LiveReader() + output_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + def lpush(buffer, byte): + def lpush_iter(buffer, pos, byte): + if pos < len(buffer): + current_byte = buffer[pos] + buffer[pos] = byte + return lpush_iter(buffer, pos+1, current_byte) + else: + return buffer + + return lpush_iter(buffer, 0, byte) + + audio_bytes = input_stream.read_bytes(BUFFER_SIZE) + + while audio_bytes: + buf = numpy.frombuffer(audio_bytes, dtype=numpy.int8) + window = numpy.hanning(len(buf)) + buf = buf * window + fourier = numpy.fft.rfft(buf, 8) + + power = numpy.abs(fourier) + spectrum = (power * 255/power.max()).astype(numpy.int8).clip(0) + element = bytearray([spectrum[1], spectrum[2], spectrum[3]]) + + display_buffer = lpush(display_buffer, element) + buf = "" + for pixel in display_buffer: + buf += struct.pack('BBB', *pixel) + output_socket.sendto( + buf, + (LED_DRIVER_HOST, LED_DRIVER_PORT) + ) + audio_bytes = input_stream.read_bytes(BUFFER_SIZE) + + +class AudioReader(object): + def read_bytes(self, buffer_size): + pass + + +class FileReader(AudioReader): + def __init__(self, file_name): + self._fp = wave.open(file_name, 'r') + + def read_bytes(self, buffer_size): + return self._fp.readframes(buffer_size) + + +class LiveReader(AudioReader): + def __init__(self): + self._paudio = pyaudio.PyAudio() + self._stream = self._paudio.open( + format=pyaudio.paInt8, + channels=1, + rate=16000, + input=True + ) + + def read_bytes(self, buffer_size): + return self._stream.read(buffer_size) + +main() \ No newline at end of file diff --git a/visualize_audio_server.py b/visualize_audio_server.py new file mode 100644 index 0000000..fc5bb02 --- /dev/null +++ b/visualize_audio_server.py @@ -0,0 +1,33 @@ +import socket +import struct +from raspledstrip.LPD8806 import LPD8806SPI +led_strip = LPD8806SPI(36) + +HOST = '' +PORT = 50007 + +input_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +input_socket.bind((HOST, PORT)) +display_buffer = [bytearray([0x00, 0x00, 0x00]) for i in xrange(36)] +gamma = [0x80 | int(pow(float(i) / 255.0, 2.5) * 127.0 + 0.5) for i in range(256)] + + +def lpush(buffer, byte): + def lpush_iter(buffer, pos, byte): + if pos < len(buffer): + current_byte = buffer[pos] + buffer[pos] = byte + return lpush_iter(buffer, pos+1, current_byte) + else: + return buffer + + return lpush_iter(buffer, 0, byte) + + +while 1: + byte_buffer, from_addr = input_socket.recvfrom(36*3) + for i in xrange(36): + pixel = struct.unpack('BBB', byte_buffer[i*3:(i*3 + 3)]) + display_buffer = lpush(display_buffer, bytearray([gamma[pixel[0]], gamma[pixel[1]], gamma[pixel[2]]])) + led_strip.update(display_buffer) + #print display_buffer \ No newline at end of file