]> vault307.fbx.one Git - Sensory_Wall.git/blob - cpxtest.py
more sensory wall projects
[Sensory_Wall.git] / cpxtest.py
1 # SPDX-FileCopyrightText: 2017 Dan Halbert for Adafruit Industries
2 # SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries
3 # SPDX-FileCopyrightText: 2017 Kattni Rembor for Adafruit Industries
4 #
5 # SPDX-License-Identifier: MIT
6
7 # The MIT License (MIT)
8 #
9 # Copyright (c) 2017 Dan Halbert for Adafruit Industries
10 # Copyright (c) 2017 Kattni Rembor, Tony DiCola for Adafruit Industries
11 #
12 # Permission is hereby granted, free of charge, to any person obtaining a copy
13 # of this software and associated documentation files (the "Software"), to deal
14 # in the Software without restriction, including without limitation the rights
15 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 # copies of the Software, and to permit persons to whom the Software is
17 # furnished to do so, subject to the following conditions:
18 #
19 # The above copyright notice and this permission notice shall be included in
20 # all copies or substantial portions of the Software.
21 #
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 # THE SOFTWARE.
29
30 # Circuit Playground Sound Meter
31
32 import array
33 import math
34 import audiobusio
35 import board
36 import neopixel
37
38 # Color of the peak pixel.
39 PEAK_COLOR = (100, 0, 255)
40 # Number of total pixels - 10 build into Circuit Playground
41 NUM_PIXELS = 10
42
43 # Exponential scaling factor.
44 # Should probably be in range -10 .. 10 to be reasonable.
45 CURVE =4
46 SCALE_EXPONENT = math.pow(10, CURVE * -0.1)
47
48 # Number of samples to read at once.
49 NUM_SAMPLES = 160
50
51
52 # Restrict value to be between floor and ceiling.
53 def constrain(value, floor, ceiling):
54 return max(floor, min(value, ceiling))
55
56
57 # Scale input_value between output_min and output_max, exponentially.
58 def log_scale(input_value, input_min, input_max, output_min, output_max):
59 normalized_input_value = (input_value - input_min) / \
60 (input_max - input_min)
61 return output_min + \
62 math.pow(normalized_input_value, SCALE_EXPONENT) \
63 * (output_max - output_min)
64
65
66 # Remove DC bias before computing RMS.
67 def normalized_rms(values):
68 minbuf = int(mean(values))
69 samples_sum = sum(
70 float(sample - minbuf) * (sample - minbuf)
71 for sample in values
72 )
73
74 return math.sqrt(samples_sum / len(values))
75
76
77 def mean(values):
78 return sum(values) / len(values)
79
80
81 def volume_color(volume):
82 return 200, volume * (255 // NUM_PIXELS), 0
83
84
85 # Main program
86
87 # Set up NeoPixels and turn them all off.
88 pixels = neopixel.NeoPixel(board.NEOPIXEL, NUM_PIXELS, brightness=0.1, auto_write=False)
89 pixels.fill(0)
90 pixels.show()
91
92 mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA,
93 sample_rate=16000, bit_depth=16)
94
95 # Record an initial sample to calibrate. Assume it's quiet when we start.
96 samples = array.array('H', [0] * NUM_SAMPLES)
97 mic.record(samples, len(samples))
98 # Set lowest level to expect, plus a little.
99 input_floor = normalized_rms(samples) + 10
100 # OR: used a fixed floor
101 # input_floor = 50
102
103 # You might want to print the input_floor to help adjust other values.
104 # print(input_floor)
105
106 # Corresponds to sensitivity: lower means more pixels light up with lower sound
107 # Adjust this as you see fit.
108 input_ceiling = input_floor + 500
109
110 peak = 0
111 while True:
112 mic.record(samples, len(samples))
113 magnitude = normalized_rms(samples)
114 # You might want to print this to see the values.
115 # print(magnitude)
116
117 # Compute scaled logarithmic reading in the range 0 to NUM_PIXELS
118 c = log_scale(constrain(magnitude, input_floor, input_ceiling),
119 input_floor, input_ceiling, 0, NUM_PIXELS)
120
121 # Light up pixels that are below the scaled and interpolated magnitude.
122 pixels.fill(0)
123 for i in range(NUM_PIXELS):
124 if i < c:
125 pixels[i] = volume_color(i)
126 # Light up the peak pixel and animate it slowly dropping.
127 if c >= peak:
128 peak = min(c, NUM_PIXELS - 1)
129 elif peak > 0:
130 peak = peak - 1
131 if peak > 0:
132 pixels[int(peak)] = PEAK_COLOR
133 pixels.show()