/ Sound_Reactive_Ears / code.py
code.py
1 # SPDX-FileCopyrightText: 2018 Dave Astels for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 """ 6 Circuit Playground Express sounds activated ears. 7 8 Adafruit invests time and resources providing this open source code. 9 Please support Adafruit and open source hardware by purchasing 10 products from Adafruit! 11 12 Written by Dave Astels for Adafruit Industries 13 Copyright (c) 2018 Adafruit Industries 14 Licensed under the MIT license. 15 16 All text above must be included in any redistribution. 17 """ 18 19 import time 20 import math 21 import array 22 import board 23 import audiobusio 24 import pwmio 25 from adafruit_motor import servo 26 from adafruit_circuitplayground.express import cpx 27 28 # Exponential scaling factor. 29 # Should probably be in range -10 .. 10 to be reasonable. 30 CURVE = 2 31 SCALE_EXPONENT = math.pow(10, CURVE * -0.1) 32 33 # Number of samples to read at once. 34 NUM_SAMPLES = 90 35 36 # the trigger threshhold 37 THRESHOLD = 6 38 left_pwm = pwmio.PWMOut(board.A1, frequency=50) 39 right_pwm = pwmio.PWMOut(board.A2, frequency=50) 40 41 left_ear = servo.Servo(left_pwm) 42 right_ear = servo.Servo(right_pwm) 43 44 cpx.pixels.fill((0, 0, 0)) 45 left_ear.angle = 0 46 right_ear.angle = 0 47 48 # Restrict value to be between floor and ceiling. 49 50 def constrain(value, floor, ceiling): 51 return max(floor, min(value, ceiling)) 52 53 54 def log_scale(input_value, input_min, input_max, output_min, output_max): 55 normalized_input_value = (input_value - input_min) / \ 56 (input_max - input_min) 57 return output_min + \ 58 math.pow(normalized_input_value, SCALE_EXPONENT) \ 59 * (output_max - output_min) 60 61 62 # Remove DC bias before computing RMS. 63 64 def normalized_rms(values): 65 minbuf = int(mean(values)) 66 samples_sum = sum( 67 float(sample - minbuf) * (sample - minbuf) 68 for sample in values 69 ) 70 71 return math.sqrt(samples_sum / len(values)) 72 73 74 def mean(values): 75 return sum(values) / len(values) 76 77 78 mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, 79 sample_rate=16000, bit_depth=16) 80 81 82 # Record an initial sample to calibrate. Assume it's quiet when we start. 83 samples = array.array('H', [0] * NUM_SAMPLES) 84 mic.record(samples, len(samples)) 85 # Set lowest level to expect, plus a little. 86 input_floor = normalized_rms(samples) + 10 87 88 # Corresponds to sensitivity: lower means ears perk up at lower volumes 89 # Adjust this as you see fit. 90 input_ceiling = input_floor + 750 91 92 ears_up = False 93 94 while True: 95 samples_read = mic.record(samples, len(samples)) 96 if samples_read < NUM_SAMPLES: 97 print("MISSING SAMPLES, only: {0}".format(samples_read)) 98 magnitude = normalized_rms(samples) 99 # You might want to print this to see the values. 100 # print(magnitude) 101 102 # Compute scaled logarithmic reading in the range 0 to 10 103 c = log_scale(constrain(magnitude, input_floor, input_ceiling), 104 input_floor, input_ceiling, 0, 10) 105 106 107 if c >= THRESHOLD and not ears_up: 108 ears_up = True 109 left_ear.angle = 90 110 right_ear.angle = 90 111 time.sleep(1.0) 112 elif c < THRESHOLD and ears_up: 113 ears_up = False 114 left_ear.angle = 0 115 right_ear.angle = 0 116 time.sleep(1.0) 117 else: 118 time.sleep(0.1)