Avrai bisogno di ispezionare il file WAV di capire quando la voce è presente. Il modo più semplice per farlo è cercare periodi rumorosi e silenziosi. Poiché il suono funziona con le onde, quando è silenzioso i valori nel file wave non cambieranno molto e quando sarà forte cambieranno molto.
Un modo per stimare il volume è variance. Come puoi vedere nell'articolo, questo può essere definito come E[(X - mu)^2]
, che potrebbe essere scritto average((X - average(X))^2)
. Qui, X è il valore del segnale in un dato punto (i valori memorizzati nel file WAV, chiamato sample
nel codice). Se sta cambiando molto, la varianza sarà grande.
Ciò consente di calcolare il volume di un intero file. Tuttavia, si desidera tenere traccia di quanto è alto il file in un dato momento, il che significa che è necessario un modulo di moving average. Un modo semplice per ottenere questo è con un first-order low-pass filter.
Non ho testato il codice qui sotto, quindi è estremamente improbabile che funzioni, ma dovrebbe iniziare. Carica il file WAV, utilizza filtri passa-basso per tracciare la media e la varianza, e funziona quando la varianza va sopra e sotto una certa soglia. Quindi, durante la riproduzione del file WAV, tiene traccia del tempo trascorso dall'inizio della riproduzione e stampa se il file WAV è rumoroso o silenzioso.
Ecco cosa si potrebbe ancora bisogno di fare:
- correggere tutti i miei errori intenzionali nel codice
- Aggiungere qualcosa di utile per reagire alle forti/modifiche tranquille
- modificare la soglia e reaction_time a ottengono buoni risultati con l'audio
- Aggiungere un po 'di hysteresis (una soglia variabile) per fermare la luce tremolante
Spero che questo aiuti!
import wave
import struct
import time
def get_loud_times(wav_path, threshold=10000, time_constant=0.1):
'''Work out which parts of a WAV file are loud.
- threshold: the variance threshold that is considered loud
- time_constant: the approximate reaction time in seconds'''
wav = wave.open(wav_path, 'r')
length = wav.getnframes()
samplerate = wav.getframerate()
assert wav.getnchannels() == 1, 'wav must be mono'
assert wav.getsampwidth() == 2, 'wav must be 16-bit'
# Our result will be a list of (time, is_loud) giving the times when
# when the audio switches from loud to quiet and back.
is_loud = False
result = [(0., is_loud)]
# The following values track the mean and variance of the signal.
# When the variance is large, the audio is loud.
mean = 0
variance = 0
# If alpha is small, mean and variance change slower but are less noisy.
alpha = 1/(time_constant * float(sample_rate))
for i in range(length):
sample_time = float(i)/samplerate
sample = struct.unpack('<h', wav.readframes(1))
# mean is the average value of sample
mean = (1-alpha) * mean + alpha * sample
# variance is the average value of (sample - mean) ** 2
variance = (1-alpha) * variance + alpha * (sample - mean) ** 2
# check if we're loud, and record the time if this changes
new_is_loud = variance > threshold
if is_loud != new_is_loud:
result.append((sample_time, new_is_loud))
is_loud = new_is_loud
return result
def play_sentence(wav_path):
loud_times = get_loud_times(wav_path)
pygame.mixer.music.load(wav_path)
start_time = time.time()
pygame.mixer.music.play()
for (t, is_loud) in loud_times:
# wait until the time described by this entry
sleep_time = start_time + t - time.time()
if sleep_time > 0:
time.sleep(sleep_time)
# do whatever
print 'loud' if is_loud else 'quiet'
Per me questa sembra una domanda per il Raspberry Pi SE ... Non so perché è stata migrata. – NULL
Se comprendo la clausola "music.get_busy() == True" while, verrà eseguita quando viene riprodotto il file .wav. Quindi inseriresti i tuoi comandi motore nel ciclo while ... giusto ... o mi manchi qualcosa? – NULL
Grazie a @NULL per la risposta. 'music.get_busy() == True' sarà vero tutto il tempo, dal momento che il suono inizia fino alla fine. Ma voglio rilevare i silenzi tra le parole, non voglio essere la bocca che si muove automaticamente tutto il tempo. Voglio smettere di muovermi mentre la frase è in silenzio. – cor