/ Sound_Reactive_NeoPixel_Peace_Pendant / code.py
code.py
1 # SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 import array 6 from rainbowio import colorwheel 7 import board 8 import neopixel 9 from analogio import AnalogIn 10 11 led_pin = board.D0 # NeoPixel LED strand is connected to GPIO #0 / D0 12 n_pixels = 12 # Number of pixels you are using 13 dc_offset = 0 # DC offset in mic signal - if unusure, leave 0 14 noise = 100 # Noise/hum/interference in mic signal 15 samples = 60 # Length of buffer for dynamic level adjustment 16 top = n_pixels + 1 # Allow dot to go slightly off scale 17 18 peak = 0 # Used for falling dot 19 dot_count = 0 # Frame counter for delaying dot-falling speed 20 vol_count = 0 # Frame counter for storing past volume data 21 22 lvl = 10 # Current "dampened" audio level 23 min_level_avg = 0 # For dynamic adjustment of graph low & high 24 max_level_avg = 512 25 26 # Collection of prior volume samples 27 vol = array.array('H', [0] * samples) 28 29 mic_pin = AnalogIn(board.A1) 30 31 strip = neopixel.NeoPixel(led_pin, n_pixels, brightness=.1, auto_write=True) 32 33 34 def remap_range(value, leftMin, leftMax, rightMin, rightMax): 35 # this remaps a value from original (left) range to new (right) range 36 # Figure out how 'wide' each range is 37 leftSpan = leftMax - leftMin 38 rightSpan = rightMax - rightMin 39 40 # Convert the left range into a 0-1 range (int) 41 valueScaled = int(value - leftMin) / int(leftSpan) 42 43 # Convert the 0-1 range into a value in the right range. 44 return int(rightMin + (valueScaled * rightSpan)) 45 46 47 while True: 48 n = int((mic_pin.value / 65536) * 1000) # 10-bit ADC format 49 n = abs(n - 512 - dc_offset) # Center on zero 50 51 if n >= noise: # Remove noise/hum 52 n = n - noise 53 54 # "Dampened" reading (else looks twitchy) - divide by 8 (2^3) 55 lvl = int(((lvl * 7) + n) / 8) 56 57 # Calculate bar height based on dynamic min/max levels (fixed point): 58 height = top * (lvl - min_level_avg) / (max_level_avg - min_level_avg) 59 60 # Clip output 61 if height < 0: 62 height = 0 63 elif height > top: 64 height = top 65 66 # Keep 'peak' dot at top 67 if height > peak: 68 peak = height 69 70 # Color pixels based on rainbow gradient 71 for i in range(0, len(strip)): 72 if i >= height: 73 strip[i] = [0, 0, 0] 74 else: 75 strip[i] = colorwheel(remap_range(i, 0, (n_pixels - 1), 30, 150)) 76 77 # Save sample for dynamic leveling 78 vol[vol_count] = n 79 80 # Advance/rollover sample counter 81 vol_count += 1 82 83 if vol_count >= samples: 84 vol_count = 0 85 86 # Get volume range of prior frames 87 min_level = vol[0] 88 max_level = vol[0] 89 90 for i in range(1, len(vol)): 91 if vol[i] < min_level: 92 min_level = vol[i] 93 elif vol[i] > max_level: 94 max_level = vol[i] 95 96 # minlvl and maxlvl indicate the volume range over prior frames, used 97 # for vertically scaling the output graph (so it looks interesting 98 # regardless of volume level). If they're too close together though 99 # (e.g. at very low volume levels) the graph becomes super coarse 100 # and 'jumpy'...so keep some minimum distance between them (this 101 # also lets the graph go to zero when no sound is playing): 102 if (max_level - min_level) < top: 103 max_level = min_level + top 104 105 # Dampen min/max levels - divide by 64 (2^6) 106 min_level_avg = (min_level_avg * 63 + min_level) >> 6 107 # fake rolling average - divide by 64 (2^6) 108 max_level_avg = (max_level_avg * 63 + max_level) >> 6 109 110 print(n)