I have already made a program that prints the max amplitude or volume, then I made a program that only prints the volume when the threshold is above 2300, now I want to make a program that closes the stream when the threshold is below 2300 for 2 seconds, so not right after it but in 2 seconds only if the threshold hasn't raised again within 2 seconds. Here is what I have:
import pyaudio
import struct
import audioop
import time
INITIAL_THRESHOLD = 0.010
FORMAT = pyaudio.paInt16
SHORT_NORMALIZE = (1.0/32768.0)
CHANNELS = 2
RATE = 44100
INPUT_BLOCK_TIME = 0.05
INPUT_FRAMES_PER_BLOCK = int(RATE*INPUT_BLOCK_TIME)
OVERSENSITIVE = 15.0/INPUT_BLOCK_TIME
UNDERSENSITIVE = 120.0/INPUT_BLOCK_TIME
MAX_BLOCKS = 0.15/INPUT_BLOCK_TIME
class TEST(object):
def __init__(self):
self.pa = pyaudio.PyAudio()
self.stream = self.open_mic_stream()
self.tap_threshold = INITIAL_THRESHOLD
self.noisycount = MAX_BLOCKS+1
self.quietcount = 0
self.errorcount = 0
def stop(self):
self.stream.close()
def find_input_device(self):
device_index = None
for i in range( self.pa.get_device_count() ):
devinfo = self.pa.get_device_info_by_index(i)
print( "Device %d: %s"%(i,devinfo["name"]) )
for keyword in ["mic","input"]:
if keyword in devinfo["name"].lower():
print( "Found an input: device %d - %s"%(i,devinfo["name"]) )
device_index = i
return device_index
if device_index == None:
print( "No preferred input found; using default input device." )
return device_index
def open_mic_stream( self ):
device_index = self.find_input_device()
stream = self.pa.open( format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
input_device_index = device_index,
frames_per_buffer = INPUT_FRAMES_PER_BLOCK)
return stream
def listen(self):
try:
chunk = self.stream.read(INPUT_FRAMES_PER_BLOCK)
except IOError, e:
self.errorcount += 1
print( "(%d) Error recording: %s"%(self.errorcount,e) )
self.noisycount = 1
return
while True:
mx = audioop.max(chunk, 2)
if mx > 2300: #print the volume level whenever the volume is above 2300
print(mx)
elif mx < 2300: #If the volume is below 2300:
time.sleep(2) #Wait two seconds
if mx > 2300: #If after two seconds the volume is back up, repeat
continue
elif mx < 2300: #If after two seconds the volume is still down, break the loop
break
print("You're Done")
self.stream.close()
print("Stream Closed")
if __name__ == "__main__":
tt = TEST()
for i in range(1000):
tt.listen()
All that does is either print the initial volume level, over and over forever, or it just closes the stream immediately. This depends on whether there is sound when I start the program or not.
EDIT:
While the methods posted in my answer outline ways to break your loop, the real reason your program is not working, is not because of the loop, its because the portion of code where you actually read the audio input is not even in your loop.
You need to read the input stream each time around the loop otherwise you will just keep making a check against the value when when the listen method is called.
def listen(self):
while True:
try:
chunk = self.stream.read(INPUT_FRAMES_PER_BLOCK)
except IOError, e:
self.errorcount += 1
print( "(%d) Error recording: %s"%(self.errorcount,e) )
self.noisycount = 1
return
mx = audioop.max(chunk, 2)
if mx > 2300: #print the volume level whenever the volume is above 2300
print(mx)
elif mx < 2300: #If the volume is below 2300:
time.sleep(2) #Wait two seconds
if mx > 2300: #If after two seconds the volume is back up, repeat
continue
You need to listen during those two seconds, instead of passively waiting.
start = -1
while True:
try:
chunk = self.stream.read(INPUT_FRAMES_PER_BLOCK)
except IOError, e:
self.errorcount += 1
print( "(%d) Error recording: %s"%(self.errorcount,e) )
self.noisycount = 1
return
mx = audioop.max(chunk, 2)
if mx > 2300: #print the volume level whenever the volume is above 2300
print(mx)
start = -1
elif mx < 2300: #If the volume is below 2300:
if start < 0: # INITIALIZE
start = time.time()
else:
if time.time() - start >= 2: #Wait two seconds
break
The problem is you aren't updating mx while (or after) you sleep. You should set a variable for when you last got a big sound, and break/restart based on new samples
last_sound = time.time() # time of last loud sound
while True:
mx = audioop.max(chunk, 2) # new sample
if mx > 2300:
print(mx)
last_sound = time.time() # reset the "timer" because we're loud
else: # I changed this to else
now = time.time()
if now - last_sound >= 2:
break # two seconds of silence!
else:
print(now-last_sound) # this should count up to 2
Related
I'm new to Python and posting on SO.
I'm doing a class project on trying to create an automated climate control system with a Raspberry pi(Pi4 4GB) and a DHT.
Here is my current error and code and I'm not sure what's causing the fail in the loop. It works for a while then errors out. It seems to error when it gets a bad read from the sensor, but until then it works fine.
Traceback (most recent call last):
File "/home/pi/raphael-kit/python/Climatecontrol.py", line 156, in <module>
main()
File "/home/pi/raphael-kit/python/Climatecontrol.py", line 118, in main
humidity, temperature = readDht11()
TypeError: cannot unpack non-iterable bool object
import RPi.GPIO as GPIO
import time
import LCD1602
dhtPin = 17
relayPin = 23 # Set GPIO23 as control pin
set_temp = 25 # this is the required temperature
GPIO.setmode(GPIO.BCM)
# Set relayPin's mode to output,
# and initial level to High(3.3v)
GPIO.setup(relayPin, GPIO.OUT, initial=GPIO.HIGH)
MAX_UNCHANGE_COUNT = 100
STATE_INIT_PULL_DOWN = 1
STATE_INIT_PULL_UP = 2
STATE_DATA_FIRST_PULL_DOWN = 3
STATE_DATA_PULL_UP = 4
STATE_DATA_PULL_DOWN = 5
def readDht11():
GPIO.setup(dhtPin, GPIO.OUT)
GPIO.output(dhtPin, GPIO.HIGH)
time.sleep(0.05)
GPIO.output(dhtPin, GPIO.LOW)
time.sleep(0.02)
GPIO.setup(dhtPin, GPIO.IN, GPIO.PUD_UP)
unchanged_count = 0
last = -1
data = []
while True:
current = GPIO.input(dhtPin)
data.append(current)
if last != current:
unchanged_count = 0
last = current
else:
unchanged_count += 1
if unchanged_count > MAX_UNCHANGE_COUNT:
break
state = STATE_INIT_PULL_DOWN
lengths = []
current_length = 0
for current in data:
current_length += 1
if state == STATE_INIT_PULL_DOWN:
if current == GPIO.LOW:
state = STATE_INIT_PULL_UP
else:
continue
if state == STATE_INIT_PULL_UP:
if current == GPIO.HIGH:
state = STATE_DATA_FIRST_PULL_DOWN
else:
continue
if state == STATE_DATA_FIRST_PULL_DOWN:
if current == GPIO.LOW:
state = STATE_DATA_PULL_UP
else:
continue
if state == STATE_DATA_PULL_UP:
if current == GPIO.HIGH:
current_length = 0
state = STATE_DATA_PULL_DOWN
else:
continue
if state == STATE_DATA_PULL_DOWN:
if current == GPIO.LOW:
lengths.append(current_length)
state = STATE_DATA_PULL_UP
else:
continue
if len(lengths) != 40:
#print ("Data not good, skip")
return False
shortest_pull_up = min(lengths)
longest_pull_up = max(lengths)
halfway = (longest_pull_up + shortest_pull_up) / 2
bits = []
the_bytes = []
byte = 0
for length in lengths:
bit = 0
if length > halfway:
bit = 1
bits.append(bit)
#print ("bits: %s, length: %d" % (bits, len(bits)))
for i in range(0, len(bits)):
byte = byte << 1
if (bits[i]):
byte = byte | 1
else:
byte = byte | 0
if ((i + 1) % 8 == 0):
the_bytes.append(byte)
byte = 0
#print (the_bytes)
checksum = (the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3]) & 0xFF
#if the_bytes[4] != checksum:
#print ("Data not good, skip")
#return False
return the_bytes[0], the_bytes[2]
def main():
while True:
humidity, temperature = readDht11()
if humidity is not None and temperature is not None:
print("Temp={0:0.1f}*C Humidity={1:0.1f}%".format(temperature, humidity))
# test for low temperature
if temperature < set_temp:
print(GPIO.output(relayPin, GPIO.LOW))
# test for high temperature
if temperature > (set_temp + 1):
print(GPIO.output(relayPin, GPIO.HIGH))
else:
print("Failed to retrieve data from humidity sensor")
time.sleep(5) #this is the time between taking readings and acting on them you can reduce it but not below 5 seconds
# Define a destroy function for clean up everything after
# the script finished
def setup():
LCD1602.init(0x27, 1) # init(slave address, background light)
LCD1602.write(0, 0, 'Temperature: %s C')
LCD1602.write(1, 1, 'humidity: %s %%')
time.sleep(2)
def destroy():
# set relay to off
GPIO.output(relayPin, GPIO.LOW)
# Release resource
GPIO.cleanup()
if __name__ == '__main__':
try:
setup()
except KeyboardInterrupt:
destroy()
as a note I haven't managed to get the LCD working in tandem but im more worried about the main functionality
I am wondering how to calculate the amount of time it would take to example:
Complete a brute force word list.
I know how to use the time function and measure in time,
but the problem is i need to find out how long it would take in the program itself...
Here is the code i made this yesterday
import itertools, math
import os
Alphabet = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") # Add or remove whatevs you think will be in the password you're cracking (example, [symbols])
counter = 1
CharLength = 1
range_num = int(raw_input("Enter range: "))
stopper = range_num + 1
filename = "bruteforce_%r.txt" % (range_num)
f = open(filename, 'a')
#n_1 = len(Alphabet)
#n_2 = n_1 - 1 # <-- total useless peice of garbage that could of been great in vurtual life
#n_3 = '0' * n_2
#n = '1' + n_3
x = range_num
y = len(Alphabet)
amount = math.pow(y, x)
total_items = math.pow(y, x)
for CharLength in range(range_num, stopper):
passwords = (itertools.product(Alphabet, repeat = CharLength))
for i in passwords:
counter += 1
percentage = (counter / total_items) * 100
amount -= 1
i = str(i)
i = i.replace("[", "")
i = i.replace("]", "")
i = i.replace("'", "")
i = i.replace(" ", "")
i = i.replace(",", "")
i = i.replace("(", "")
i = i.replace(")", "")
f.write(i)
f.write('\n')
print "Password: %r\tPercentage: %r/100\tAmount left: %r" % (i, int(percentage), amount)
if i == '0'* range_num:
print "*Done"
f.close()
exit(0)
else:
pass
This is my timer function i managed to make
#import winsound # Comment this out if your using linux
import os
import time
from sys import exit
print "This is the timer\nHit CTRL-C to stop the timer\nOtherwise just let it rip untill the time's up"
hours = int(raw_input('Enter the hours.\n>>> '))
os.system('clear') # Linux
#os.system('cls') # Windows
minutes = int(raw_input('Enter the minutes.\n>>> '))
os.system('clear') # linux
#os.system('cls') # Windows
seconds = int(raw_input('Enter the seconds.\n>>> '))
os.system('clear') # Linux
#os.system('cls') # Windows
stop_time = '%r:%r:%r' % (hours, minutes, seconds)
t_hours = 00
t_minutes = 00
t_seconds = 00
while t_seconds <= 60:
try:
os.system('clear') # Linux
#os.system('cls') # Windows
current_time = '%r:%r:%r' % (t_hours, t_minutes, t_seconds)
print current_time
time.sleep(1)
t_seconds+=1
if current_time == stop_time:
print "// Done"
#winsound.Beep(500,1000)
#winsound.Beep(400,1000)
break
elif t_seconds == 60:
t_minutes+=1
t_seconds=0
elif t_minutes == 60:
t_hours+=1
t_minutes = 00
except KeyboardInterrupt:
print "Stopped at: %r:%r:%r" % (t_hours, t_minutes, t_seconds)
raw_input("Hit enter to continue\nHit CTRL-C to end")
try:
pass
except KeyboardInterrupt:
exit(0)
Now i just cant figure out how to make this again but to calculate how long it will take rather than how long it is taking...
You cannot predict the time a script is going to take.
Firstly because two machines wouldn't run the script in the same time, and secondly, because the execution time on one machine can vary from on take to another.
What you can do, however, is compute the percentage of execution.
You need to figure out, for example, how many iterations your main loop will do, and calculate at each iteration the ratio current iteration count / total number of iterations.
Here is a minimalist example of what you can do:
n = 10000
for i in range(n):
print("Processing file {} ({}%)".format(i, 100*i//n))
process_file(i)
You can take it further and add the time as an additional info:
n = 10000
t0 = time.time()
for i in range(n):
t1 = time.time()
print("Processing file {} ({}%)".format(i, 100*i//n), end="")
process_file(i)
t2 = time.time()
print(" {}s (total: {}s)".format(t2-t1, t2-t0))
The output will look like this:
...
Processing file 2597 (25%) 0.2s (total: 519.4s)
Processing file 2598 (25%) 0.3s (total: 519.7s)
Processing file 2599 (25%) 0.1s (total: 519.8s)
Processing file 2600 (25%)
This is my implementation, which returns time elapsed, time left, and finish time in H:M:S format.
def calcProcessTime(starttime, cur_iter, max_iter):
telapsed = time.time() - starttime
testimated = (telapsed/cur_iter)*(max_iter)
finishtime = starttime + testimated
finishtime = dt.datetime.fromtimestamp(finishtime).strftime("%H:%M:%S") # in time
lefttime = testimated-telapsed # in seconds
return (int(telapsed), int(lefttime), finishtime)
Example:
import time
import datetime as dt
start = time.time()
cur_iter = 0
max_iter = 10
for i in range(max_iter):
time.sleep(5)
cur_iter += 1
prstime = calcProcessTime(start,cur_iter ,max_iter)
print("time elapsed: %s(s), time left: %s(s), estimated finish time: %s"%prstime)
Output:
time elapsed: 5(s), time left: 45(s), estimated finish time: 14:28:18
time elapsed: 10(s), time left: 40(s), estimated finish time: 14:28:18
time elapsed: 15(s), time left: 35(s), estimated finish time: 14:28:18
....
You will never ever be able to know exactly how long it is going to take to finish. The best you can do is calculate was percentage of the work you have finished and how long that has taken you and then project that out.
For example if you are doing some work on the range of numbers from 1 to 100 you could do something such as
start_time = get the current time
for i in range(1, 101):
# Do some work
current_time = get the current time
elapsed_time = current_time - start_time
time_left = 100 * elapsed_time / i - elapsed_time
print(time_left)
Please understand that the above is largely pseudo-code
The following function will calculate the remaining time:
last_times = []
def get_remaining_time(i, total, time):
last_times.append(time)
len_last_t = len(last_times)
if len_last_t > 5:
last_times.pop(0)
mean_t = sum(last_times) // len_last_t
remain_s_tot = mean_t * (total - i + 1)
remain_m = remain_s_tot // 60
remain_s = remain_s_tot % 60
return f"{remain_m}m{remain_s}s"
The parameters are:
i : The current iteration
total : the total number of iterations
time : the duration of the last iteration
It uses the average time taken by the last 5 iterations to calculate the remaining time. You can the use it in your code as follows:
last_t = 0
iterations = range(1,1000)
for i in iterations:
t = time.time()
# Do your task here
last_t = time.time() - t
get_remaining_time(i, len(iterations), last_t)
I have an AM radio that is transferring one of two frequencies: 1047hz or 2093hz and I am trying to record the audio and use python to determine which frequency is being played (each signal is 1 second long). The problem is the audio is very noisy especially around the 800-1000hz range and it it interfering with me getting the correct signal. How can I filter out the noise? Here is my code:
import sys, math
from aubio import source, pitch, freqtomidi
if len(sys.argv) < 2:
print "Usage: %s <filename> [samplerate]" % sys.argv[0]
sys.exit(1)
filename = sys.argv[1]
downsample = 1
samplerate = 44100 / downsample
if len( sys.argv ) > 2: samplerate = int(sys.argv[2])
win_s = 4096 / downsample # fft size
hop_s = 512 / downsample # hop size
s = source(filename, samplerate, hop_s)
samplerate = s.samplerate
tolerance = 0.8
pitch_o = pitch("yin", win_s, hop_s, samplerate)
pitch_o.set_tolerance(tolerance)
pitches = []
confidences = []
total_frames = 0
while True:
samples, read = s()
pitch = pitch_o(samples)[0]
pitch = int(round(pitch))
confidence = pitch_o.get_confidence()
#if confidence < 0.6: pitch = 0.
print "%f %f %f" % (total_frames / float(samplerate), pitch, confidence)
pitches.append((total_frames/float(samplerate),pitch))
confidences += [confidence]
total_frames += read
if read < hop_s: break
chunks = []
average_pitches = []
for i in range(len(pitches)):
if pitches[i][1] < 950:
del pitches[i]
else:
break
for i in reversed(range(len(pitches))):
if pitches[i][1] == 0:
del pitches[i]
else:
break
seconds = math.ceil(total_frames/float(samplerate))
start = pitches[0][0]
for i in range(int(pitches[len(pitches)-1][0]-start)+1):
chunks.append([])
for pitch in pitches:
if(pitch[0]==0):
continue
chunks[int(pitch[0]-start)].append(pitch[1])
for chunk in chunks:
average_pitches.append(sum(chunk)/float(len(chunk)))
bits = []
for pitch in average_pitches:
print pitch
if 950 <= pitch < 1200:
bits.append(0)
if 1700 <= pitch <2100:
bits.append(1)
print bits
Hi I am trying to develop a leakybucket with unlimited bucket capacity in Python. I want it to be thread-safe and CPU efficient, with a minimum number of threads. It generally works now. But there are some tiny errors.
I throttle the bandwidth to 500 kbps. But the third line seems to break this. Also, can anybody tell me if this is the right way to implement leakybucket? Thanks.
rate: 500.00
rate: 500.00
rate: 550.00
rate: 500.00
rate: 500.00
rate: 500.00
rate: 500.00
rate: 500.00
Code here:
from collections import deque
import threading, time
class LeakyBucket:
'''the leaky bucket throttling the bit rate'''
def __init__(self, node, bitsPerSec, measIntv, LBtype):
self.node = node
self.bitsPerSec = bitsPerSec #the rate limit
self.measIntv = measIntv #the measure interval, tokens will become full at the beginning of each interval
self.LBtype = LBtype #the type of the bucket
self.lastTime = 0 #the start time of the last measure interval
self.bitsDone = 0 #the bits that have been transmitted
self.BDLock = threading.Lock() #the lock for the bits sent
self.packDQ = deque() #the packet Q
self.maxToken = bitsPerSec*float(measIntv) #the max token (bits)
self.token = self.maxToken #the current token
self.condition = threading.Condition() #sync lock
def packIn(self, msg):
'''Insert a packet'''
self.condition.acquire()
self.packDQ.append(msg)
self.condition.notify()
self.condition.release()
def keepPoping(self):
'''keep poping new pack'''
self.lastTime = time.time() #record the start time
while True:
timeNow = time.time()
if timeNow - self.lastTime > self.measIntv:
#new intv, need to reset token
self.token = self.maxToken
self.lastTime = timeNow
self.condition.acquire()
if self.packDQ: # the queue is not empty
pack = list(self.packDQ)[0]
packLen = len(pack[2])*8
if packLen > self.token: #no enough token?
#self.packDQ.popleft()
self.condition.release()
time.sleep(max(self.lastTime+self.measIntv-time.time(),0)) #wait for enough token
else: #enough token, can send out the packet
self.packDQ.popleft()
self.condition.release()
self.changeBitsDone(packLen)
self.token = self.token - packLen #consume token
else:
self.condition.wait()
self.condition.release()
def begin(self):
'''begin the leakybucket'''
aThread = threading.Thread(target = self.keepPoping, args = [])
aThread.start()
def getBitsDone(self):
'''get and reset bitsDone, for testing'''
self.BDLock.acquire()
reV = self.bitsDone
self.bitsDone = 0
self.BDLock.release()
return reV
def changeBitsDone(self,length):
'''change bitsDone, for testing'''
self.BDLock.acquire()
self.bitsDone += length
self.BDLock.release()
def measure(self, intv):
'''measure the throughput of the leaky bucket'''
while True:
bitsDone = self.getBitsDone()
rate = bitsDone / float(intv*1024)
print 'rate: %.2f' % rate
time.sleep(intv)
def startMeasure(self, intv):
'''start measure the rate'''
#print 'here'
aThread = threading.Thread(target = self.measure, args = [intv])
aThread.start()
#===============================
def main():
pack = 1000*'a'
msg = ('192.168.1.1', 16000, pack)
print 'here'
LB = LeakyBucket(None, 500*1024, 1, 'reg')
LB.begin()
LB.startMeasure(10)
numMsg = 0
while numMsg < 10000:
LB.packIn(msg)
#print 'pack in'
numMsg += 1
if __name__ == '__main__':
main()
How would I use pyaudio to detect a sudden tapping noise from a live microphone?
One way I've done it:
read a block of samples at a time,
say 0.05 seconds worth
compute the
RMS amplitude of the block (square
root of the average of the squares of
the individual samples)
if the block's RMS amplitude is greater than a threshold, it's a "noisy block" else it's a "quiet block"
a sudden tap would be a quiet block followed by a small number of noisy blocks followed by a quiet block
if you never get a quiet block, your threshold is too low
if you never get a noisy block, your threshold is too high
My application was recording "interesting" noises unattended, so it would record as long as there were noisy blocks. It would multiply the threshold by 1.1 if there was a 15-second noisy period ("covering its ears") and multiply the threshold by 0.9 if there was a 15-minute quiet period ("listening harder"). Your application will have different needs.
Also, just noticed some comments in my code regarding observed RMS values. On the built in mic on a Macbook Pro, with +/- 1.0 normalized audio data range, with input volume set to max, some data points:
0.003-0.006 (-50dB to -44dB) an obnoxiously loud central heating fan in my house
0.010-0.40 (-40dB to -8dB) typing on the same laptop
0.10 (-20dB) snapping fingers softly at 1' distance
0.60 (-4.4dB) snapping fingers loudly at 1'
Update: here's a sample to get you started.
#!/usr/bin/python
# open a microphone in pyAudio and listen for taps
import pyaudio
import struct
import math
INITIAL_TAP_THRESHOLD = 0.010
FORMAT = pyaudio.paInt16
SHORT_NORMALIZE = (1.0/32768.0)
CHANNELS = 2
RATE = 44100
INPUT_BLOCK_TIME = 0.05
INPUT_FRAMES_PER_BLOCK = int(RATE*INPUT_BLOCK_TIME)
# if we get this many noisy blocks in a row, increase the threshold
OVERSENSITIVE = 15.0/INPUT_BLOCK_TIME
# if we get this many quiet blocks in a row, decrease the threshold
UNDERSENSITIVE = 120.0/INPUT_BLOCK_TIME
# if the noise was longer than this many blocks, it's not a 'tap'
MAX_TAP_BLOCKS = 0.15/INPUT_BLOCK_TIME
def get_rms( block ):
# RMS amplitude is defined as the square root of the
# mean over time of the square of the amplitude.
# so we need to convert this string of bytes into
# a string of 16-bit samples...
# we will get one short out for each
# two chars in the string.
count = len(block)/2
format = "%dh"%(count)
shorts = struct.unpack( format, block )
# iterate over the block.
sum_squares = 0.0
for sample in shorts:
# sample is a signed short in +/- 32768.
# normalize it to 1.0
n = sample * SHORT_NORMALIZE
sum_squares += n*n
return math.sqrt( sum_squares / count )
class TapTester(object):
def __init__(self):
self.pa = pyaudio.PyAudio()
self.stream = self.open_mic_stream()
self.tap_threshold = INITIAL_TAP_THRESHOLD
self.noisycount = MAX_TAP_BLOCKS+1
self.quietcount = 0
self.errorcount = 0
def stop(self):
self.stream.close()
def find_input_device(self):
device_index = None
for i in range( self.pa.get_device_count() ):
devinfo = self.pa.get_device_info_by_index(i)
print( "Device %d: %s"%(i,devinfo["name"]) )
for keyword in ["mic","input"]:
if keyword in devinfo["name"].lower():
print( "Found an input: device %d - %s"%(i,devinfo["name"]) )
device_index = i
return device_index
if device_index == None:
print( "No preferred input found; using default input device." )
return device_index
def open_mic_stream( self ):
device_index = self.find_input_device()
stream = self.pa.open( format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
input_device_index = device_index,
frames_per_buffer = INPUT_FRAMES_PER_BLOCK)
return stream
def tapDetected(self):
print("Tap!")
def listen(self):
try:
block = self.stream.read(INPUT_FRAMES_PER_BLOCK)
except IOError as e:
# dammit.
self.errorcount += 1
print( "(%d) Error recording: %s"%(self.errorcount,e) )
self.noisycount = 1
return
amplitude = get_rms( block )
if amplitude > self.tap_threshold:
# noisy block
self.quietcount = 0
self.noisycount += 1
if self.noisycount > OVERSENSITIVE:
# turn down the sensitivity
self.tap_threshold *= 1.1
else:
# quiet block.
if 1 <= self.noisycount <= MAX_TAP_BLOCKS:
self.tapDetected()
self.noisycount = 0
self.quietcount += 1
if self.quietcount > UNDERSENSITIVE:
# turn up the sensitivity
self.tap_threshold *= 0.9
if __name__ == "__main__":
tt = TapTester()
for i in range(1000):
tt.listen()
a simplified version of the above code...
import pyaudio
import struct
import math
INITIAL_TAP_THRESHOLD = 0.010
FORMAT = pyaudio.paInt16
SHORT_NORMALIZE = (1.0/32768.0)
CHANNELS = 2
RATE = 44100
INPUT_BLOCK_TIME = 0.05
INPUT_FRAMES_PER_BLOCK = int(RATE*INPUT_BLOCK_TIME)
OVERSENSITIVE = 15.0/INPUT_BLOCK_TIME
UNDERSENSITIVE = 120.0/INPUT_BLOCK_TIME # if we get this many quiet blocks in a row, decrease the threshold
MAX_TAP_BLOCKS = 0.15/INPUT_BLOCK_TIME # if the noise was longer than this many blocks, it's not a 'tap'
def get_rms(block):
# RMS amplitude is defined as the square root of the
# mean over time of the square of the amplitude.
# so we need to convert this string of bytes into
# a string of 16-bit samples...
# we will get one short out for each
# two chars in the string.
count = len(block)/2
format = "%dh"%(count)
shorts = struct.unpack( format, block )
# iterate over the block.
sum_squares = 0.0
for sample in shorts:
# sample is a signed short in +/- 32768.
# normalize it to 1.0
n = sample * SHORT_NORMALIZE
sum_squares += n*n
return math.sqrt( sum_squares / count )
pa = pyaudio.PyAudio() #]
#|
stream = pa.open(format = FORMAT, #|
channels = CHANNELS, #|---- You always use this in pyaudio...
rate = RATE, #|
input = True, #|
frames_per_buffer = INPUT_FRAMES_PER_BLOCK) #]
tap_threshold = INITIAL_TAP_THRESHOLD #]
noisycount = MAX_TAP_BLOCKS+1 #|---- Variables for noise detector...
quietcount = 0 #|
errorcount = 0 #]
for i in range(1000):
try: #]
block = stream.read(INPUT_FRAMES_PER_BLOCK) #|
except IOError, e: #|---- just in case there is an error!
errorcount += 1 #|
print( "(%d) Error recording: %s"%(errorcount,e) ) #|
noisycount = 1 #]
amplitude = get_rms(block)
if amplitude > tap_threshold: # if its to loud...
quietcount = 0
noisycount += 1
if noisycount > OVERSENSITIVE:
tap_threshold *= 1.1 # turn down the sensitivity
else: # if its to quiet...
if 1 <= noisycount <= MAX_TAP_BLOCKS:
print 'tap!'
noisycount = 0
quietcount += 1
if quietcount > UNDERSENSITIVE:
tap_threshold *= 0.9 # turn up the sensitivity