Sonomètre visuel - Niveau avancé

Ce projet reprend les éléments décrits sur cette page d'Adafruit :

https://learn.adafruit.com/adafruit-circuit-playground-express/playground-sound-meter

Utilisez le microphone de votre carte cpx pour mesurer les niveaux sonores et les visualiser à l'aide des LEDs RVB.

Le programme échantillonne l'audio pendant un court instant et calcule l'énergie dans l'échantillon (correspondant au volume) à l'aide d'un calcul (fonction normalized_rms). Vous pouvez essayer de varier la taille de l'échantillon si vous le souhaitez.

Le programme prend un échantillon lorsqu'il commence à régler un niveau sonore "silencieux". Si cela ne vous convient pas, définissez input_floor comme un nombre fixe. Si le compteur semble trop sensible, essayez de changer input_ceiling = input_floor + 500 en input_ceiling = input_floor + 2000 ou plus. Ou aller dans l'autre sens.

La fonction log_scale() fait varier le nombre de NeoPixels éclairés de manière exponentielle, car les niveaux sonores peuvent varier d'un facteur 10 entre le niveau le plus fort et celui le plus faible. Essayez de modifier la façon dont elle est calculée pour voir ce qui se passe.

MéthodeLe programme

1
# The MIT License (MIT)
2
#
3
# Copyright (c) 2017 Dan Halbert for Adafruit Industries
4
# Copyright (c) 2017 Kattni Rembor, Tony DiCola for Adafruit Industries
5
#
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
# of this software and associated documentation files (the "Software"), to deal
8
# in the Software without restriction, including without limitation the rights
9
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
# copies of the Software, and to permit persons to whom the Software is
11
# furnished to do so, subject to the following conditions:
12
#
13
# The above copyright notice and this permission notice shall be included in
14
# all copies or substantial portions of the Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
# THE SOFTWARE.
23
24
import array
25
import math
26
27
import audiobusio
28
import board
29
import neopixel
30
31
# facteur d'echelle exponentielle.
32
# Les valeurs raisonables devraient être entre -10 et 10
33
CURVE = 2
34
SCALE_EXPONENT = math.pow(10, CURVE * -0.1)
35
36
PEAK_COLOR = (100, 0, 255)
37
NUM_PIXELS = 10
38
39
# Nombre d'echantillons à lire d'un coup
40
NUM_SAMPLES = 160
41
42
43
# contraint les valeurs dans un intervalle entre floor et ceilling
44
45
def constrain(value, floor, ceiling):
46
    return max(floor, min(value, ceiling))
47
48
49
# equivalent de la fonction range_map déjà rencontrée mais en echelle exponentielle 
50
51
52
def log_scale(input_value, input_min, input_max, output_min, output_max):
53
    normalized_input_value = (input_value - input_min) / \
54
                             (input_max - input_min)
55
    return output_min + \
56
        math.pow(normalized_input_value, SCALE_EXPONENT) \
57
        * (output_max - output_min)
58
59
def normalized_rms(values):
60
    minbuf = int(mean(values))
61
    samples_sum = sum(
62
        float(sample - minbuf) * (sample - minbuf)
63
        for sample in values
64
    )
65
66
    return math.sqrt(samples_sum / len(values))
67
68
69
def mean(values):
70
    return sum(values) / len(values)
71
72
73
def volume_color(volume):
74
    return 200, volume * (255 // NUM_PIXELS), 0
75
76
77
# Programme principal
78
79
80
# initialise les leds neopixels
81
pixels = neopixel.NeoPixel(board.NEOPIXEL, NUM_PIXELS,
82
                           brightness=0.1, auto_write=False)
83
pixels.fill(0)
84
pixels.show()
85
86
mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA,
87
                       sample_rate=16000, bit_depth=16)
88
89
# Calibration du niveau de silence sur un échantillon au démarrage
90
samples = array.array('H', [0] * NUM_SAMPLES)
91
mic.record(samples, len(samples))
92
# définit le niveau minimum
93
input_floor = normalized_rms(samples) + 10
94
# OU utiliser une valeur fixée
95
# input_floor = 50
96
97
# You might want to print the input_floor to help adjust other values.
98
# print(input_floor)
99
100
# sensibilité: moins -> augmente le nb de pixels 
101
input_ceiling = input_floor + 500
102
103
peak = 0
104
while True:
105
    mic.record(samples, len(samples))
106
    magnitude = normalized_rms(samples)
107
    # You might want to print this to see the values.
108
    # print(magnitude)
109
110
    # Calcule l'échelle logarithmique de 0 à NUM_PIXELS
111
    c = log_scale(constrain(magnitude, input_floor, input_ceiling),
112
                  input_floor, input_ceiling, 0, NUM_PIXELS)
113
114
    # allume les LEDs
115
    pixels.fill(0)
116
    for i in range(NUM_PIXELS):
117
        if i < c:
118
            pixels[i] = volume_color(i)
119
        if c >= peak:
120
            peak = min(c, NUM_PIXELS - 1)
121
        elif peak > 0:
122
            peak = peak - 1
123
        if peak > 0:
124
            pixels[int(peak)] = PEAK_COLOR
125
    pixels.show()
126