Browse Source

Initial commit

master
The Dod 2 years ago
commit
2f8a78d027
3 changed files with 168 additions and 0 deletions
  1. 14
    0
      README.md
  2. 68
    0
      code.py
  3. 86
    0
      lib/teaandtechtime_fft.py

+ 14
- 0
README.md View File

1
+### Spectroshaker
2
+
3
+This code for [Adafruit Circuit Playgroud Bluefruit](https://learn.adafruit.com/adafruit-circuit-playground-bluefruit)
4
+makes the RGB LEDs around it behave in one of two modes (determined by the state of the slider switch):
5
+
6
+* Spectrogram mode: LEDs are affected by low/mid/high levels of mic input (i.e. respond to music)
7
+* Shaker mode: LEDs are affected by x/y/z acceleration (i.e. respond to dancing)
8
+
9
+[video](https://www.zzzen.com/kraftz/2021-11-19-spectroshaker.webm)
10
+
11
+#### Note
12
+
13
+`lib/teaandtechtime_fft.py` comes from the [no-itertools fork](https://github.com/thedod/CircuitPython_FFT/tree/no-itertools) of Thomas Schucker's `CircuitPython_FFT`.
14
+It needs to be under the bluefruit's `lib/` folder.

+ 68
- 0
code.py View File

1
+"""
2
+Spectroshaker by The Dod:
3
+Adafruit CircuitPlayground Bluefruit dual-mode "bling"
4
+(selectable via the slider switch).
5
+The RGB levels of neopixels around the circuit indicate:
6
+- Low/Mid/High sound levels (spectrogram mode)
7
+- X/Y/Z acceleration (shaker mode)
8
+TO BE CONTINUED ...
9
+"""
10
+import math
11
+import time
12
+from adafruit_circuitplayground import cp
13
+from teaandtechtime_fft import spectrogram
14
+
15
+NUM_PIXELS = 10
16
+# Like a clock, with connectors at 12 (USB) and 6 (power)
17
+LED_ANGLES = [
18
+    i*math.pi/6.0
19
+    for i in [11, 10, 9, 8, 7, 5, 4, 3, 2, 1]]
20
+
21
+minx = miny = minz = -5.0
22
+maxx = maxy = maxz = 5.0
23
+
24
+fft_size = 8 # power of 2
25
+samples = [0.0+0.0j]*fft_size
26
+MAX_2FREQ = 10.0 # After testing this a bit in the wild
27
+
28
+cp.pixels.brightness = 0.1
29
+
30
+def angles2level(led, val):
31
+    led_angle = LED_ANGLES[led]
32
+    val_angle = val * 2.0 * math.pi
33
+    angle = led_angle - val_angle
34
+    while angle>2*math.pi:
35
+        angle -= 2*math.pi
36
+    # the pow() makes sin() values sharper
37
+    return int(255*pow(math.sin(angle/2), 4))
38
+
39
+while True:
40
+    if cp.button_a:
41
+        cp.pixels.brightness = max(0.05, cp.pixels.brightness*0.9)
42
+    elif cp.button_b:
43
+        cp.pixels.brightness = min(0.95, 1.0-(1.0-cp.pixels.brightness)*0.9)
44
+
45
+    samples.pop(0)
46
+    samples.append(cp.sound_level+0.0j)
47
+    x, y, z = cp.acceleration
48
+    if x<minx: minx = x
49
+    if y<miny: miny = y
50
+    if z<minz: minz = z
51
+    if x>maxx: maxx = x
52
+    if y>maxy: maxy = y
53
+    if z>maxx: maxz = z
54
+    if cp.switch: # accelerometer mode
55
+        vals = (
56
+            (x-minx)/(maxx-minx),
57
+            (y-miny)/(maxy-miny),
58
+            (z-minz)/(maxz-minz))
59
+    else: # spectrogram mode
60
+        raw_freqs = spectrogram(samples)
61
+        pairs = [raw_freqs[i]+raw_freqs[i+1] for i in range(fft_size//2)]
62
+        vals = [min(1.0, abs(x/MAX_2FREQ)) for x in pairs[:3]]
63
+    for i in range(NUM_PIXELS):
64
+        cp.pixels[i] = (
65
+            angles2level(i, vals[0]),
66
+            angles2level(i, vals[1]),
67
+            angles2level(i, vals[2]))
68
+    time.sleep(0.01)

+ 86
- 0
lib/teaandtechtime_fft.py View File

1
+# The MIT License (MIT)
2
+#
3
+# Copyright (c) 2019 Tom Schucker for Tea and Tech Time
4
+#
5
+# Permission is hereby granted, free of charge, to any person obtaining a copy
6
+# of this software and associated documentation files (the "Software"), to deal
7
+# in the Software without restriction, including without limitation the rights
8
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+# copies of the Software, and to permit persons to whom the Software is
10
+# furnished to do so, subject to the following conditions:
11
+#
12
+# The above copyright notice and this permission notice shall be included in
13
+# all copies or substantial portions of the Software.
14
+#
15
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+# THE SOFTWARE.
22
+"""
23
+`teaandtechtime_fft`
24
+================================================================================
25
+
26
+CircuitPython FFT Library
27
+
28
+* Author(s): Tom Schucker
29
+
30
+Implementation Notes
31
+---------------------
32
+This variant comes from @thedod's `no-itertools` fork:
33
+https://github.com/thedod/CircuitPython_FFT/tree/no-itertools
34
+
35
+**Hardware:**
36
+
37
+**Software and Dependencies:**
38
+
39
+* Adafruit CircuitPython firmware for the supported boards:
40
+  https://github.com/adafruit/circuitpython/releases
41
+
42
+"""
43
+
44
+# imports
45
+from math import pi, sin, cos, sqrt, pow, log
46
+import array
47
+
48
+__version__ = "0.0.0-auto.0"
49
+__repo__ = "https://github.com/tschucker/Teaandtechtime_CircuitPython_FFT.git"
50
+
51
+#Computes the complex fft of the input array needs to be power of 2 length to work.
52
+def fft(x):
53
+    N = len(x)
54
+    if N <= 1: return x
55
+    even = fft([x[i] for i in range(0, N, 2)])
56
+    odd =  fft([x[i] for i in range(1, N, 2)])
57
+    T = [cos(2*pi*k/N)*odd[k].real+sin(2*pi*k/N)*odd[k].imag + (cos(2*pi*k/N)*odd[k].imag-sin(2*pi*k/N)*odd[k].real)*1j for k in range(N//2)]
58
+    return [even[k].real + T[k].real + (even[k].imag + T[k].imag)*1j for k in range(N//2)] + \
59
+           [even[k].real - T[k].real + (even[k].imag - T[k].imag)*1j for k in range(N//2)]
60
+
61
+#Computes the complex inverse fft of the input array needs to be power of 2 length to work
62
+#not the most efficiant but uses the same fft code.
63
+def ifft(x):
64
+    fft_len = float(len(x))
65
+    x_swap = []
66
+    for s in x:
67
+        x_swap.append(s.imag + s.real*1j)
68
+    temp = fft(x_swap)
69
+    temp_swap = []
70
+    for s in temp:
71
+        temp_swap.append((s.imag/fft_len) + (s.real/fft_len)*1j)
72
+    return temp_swap
73
+
74
+#Computes the double sided spectrogram of the input array needs to be a power of 2 to work
75
+def spectrogram(x):
76
+    freq = fft(x)
77
+    temp_list = []
78
+    for f in freq:
79
+        abs_val = abs(f)
80
+        if  abs_val != 0.0:
81
+            temp_list.append(int(log(abs_val)))
82
+        else:
83
+            temp_list.append(0)
84
+    return temp_list
85
+
86
+

Loading…
Cancel
Save