Detect 2 frequencies from noisy audio python - python

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

Related

Combining wav files programmatically - Python

I am looking to combine 10 audio samples in various manners (format - wav probably, but this can be changed to any format as they will be pre-recorded).
from pydub import AudioSegment
sounds = []
sound1 = AudioSegment.from_wav("Dropbox/PIREAD/1.wav")
sound2 = AudioSegment.from_wav("Dropbox/PIREAD/2.wav")
sound3 = AudioSegment.from_wav("Dropbox/PIREAD/3.wav")
sound4 = AudioSegment.from_wav("Dropbox/PIREAD/4.wav")
sound5 = AudioSegment.from_wav("Dropbox/PIREAD/5.wav")
sound6 = AudioSegment.from_wav("Dropbox/PIREAD/6.wav")
sound7 = AudioSegment.from_wav("Dropbox/PIREAD/7.wav")
sound8 = AudioSegment.from_wav("Dropbox/PIREAD/8.wav")
sound9 = AudioSegment.from_wav("Dropbox/PIREAD/9.wav")
sound0 = AudioSegment.from_wav("Dropbox/PIREAD/0.wav")
sounds=[sound1,sound2,sound3,sound4,sound5,sound6,sound7,sound8,sound9,sound0]
combined_sounds = AudioSegment.empty()
for x in range(10):
for y in range(10):
combined_sounds += sounds[y]
combined_sounds.export("Dropbox/PIREAD/joinedFile.wav", format="wav")
This is literally me reading the numbers 0-9 and assembling them into one overall wav file.
It works - but it is slow once the loop is extended x=100, x=1000.
Q: How can I speed things up?
The actual order of the numbers will be read from a text$ - for example "354224848179261915075" which happens to be the 100th Fibonacci number.
Cheers
Glen
I believe it's slow because when you loop over x, you repeat operations (the loop over y) which could be computed before the loop over x, then assembled.
I looked into AudioSegment and found potentially useful method for you namely from_mono_audiosegments but it is limited to mono sounds and you will need to test if it is faster than += please compare time-wise these options, i.e.
import time
from pydub import AudioSegment
sounds = []
sound1 = AudioSegment.from_wav("Dropbox/PIREAD/1.wav")
sound2 = AudioSegment.from_wav("Dropbox/PIREAD/2.wav")
sound3 = AudioSegment.from_wav("Dropbox/PIREAD/3.wav")
sound4 = AudioSegment.from_wav("Dropbox/PIREAD/4.wav")
sound5 = AudioSegment.from_wav("Dropbox/PIREAD/5.wav")
sound6 = AudioSegment.from_wav("Dropbox/PIREAD/6.wav")
sound7 = AudioSegment.from_wav("Dropbox/PIREAD/7.wav")
sound8 = AudioSegment.from_wav("Dropbox/PIREAD/8.wav")
sound9 = AudioSegment.from_wav("Dropbox/PIREAD/9.wav")
sound0 = AudioSegment.from_wav("Dropbox/PIREAD/0.wav")
sounds=[sound1,sound2,sound3,sound4,sound5,sound6,sound7,sound8,sound9,sound0]
# option1 using +=
t1 = time.time()
combined_sounds1 = AudioSegment.empty()
for s in sounds
combined_sounds1 += s
t2 = time.time()
# end of option1
# option2 using from_mono_audiosegments
t3 = time.time()
combined_sounds2 = AudioSegment.from_mono_audiosegments(*sounds)
t4 = time.time()
# end of option2
print('option1 (seconds):',t2-t1)
print('option2 (seconds):',t4-t3)
Thanks for the suggestions and advice above. This is the final code I used and link to the resultant video (with ffmpeg visualisation):
# Program to display the Fibonacci sequence up to n-th term
from pydub import AudioSegment
combined_sounds = ""
sound1 = AudioSegment.from_wav("1_2.wav")
sound2 = AudioSegment.from_wav("2_2.wav")
sound3 = AudioSegment.from_wav("3_2.wav")
sound4 = AudioSegment.from_wav("4_2.wav")
sound5 = AudioSegment.from_wav("5_2.wav")
sound6 = AudioSegment.from_wav("6_2.wav")
sound7 = AudioSegment.from_wav("7_2.wav")
sound8 = AudioSegment.from_wav("8_2.wav")
sound9 = AudioSegment.from_wav("9_2.wav")
sound0 = AudioSegment.from_wav("0_2.wav")
nterms=1000
# first two terms
n1, n2 = 0, 1
count = 0
fib = ""
# check if the number of terms is valid
if nterms <= 0:
print("Please enter a positive integer")
# if there is only one term, return n1
elif nterms == 1:
print("Fibonacci sequence upto",nterms,":")
print(n1)
# generate fibonacci sequence
else:
print("Fibonacci sequence:")
while count < nterms:
#print(n1)
fib += str(n1)
nth = n1 + n2
# update values
n1 = n2
n2 = nth
count += 1
i=-36
j=0
fibs = [fib[i:i+1000] for i in range(0, len(fib), 1000)]
seg = 0
for a in fibs:
if seg == 2:
break
combined_sounds = AudioSegment.empty()
seg +=1
for x in a:
i,j = -36,0
s = eval("sound"+str(x))
s = s.apply_gain_stereo(i,j)
combined_sounds += s
i,j = j,i
combined_sounds.export("joinedFile"+str(seg)+".wav", format="wav")
This splits the output into 1000 digit wav files. The first 1000 Fibonacci terms produces nearly 15Gb of wavs!
Uploaded to YouTube: https://www.youtube.com/watch?v=U7Z_HOGqjlE
Thanks all.

Calculating position from IMU data in cases where GPS is not available

First post here and I'm jumping in to python with both feet.
My project is to attempt to calculate the position of a underwater robot using only IMU sensors and a speed table.
I am very new to programming and I'm sure I'll get a lot of great feedback on the attached code, but the step I'm currently stuck on is creating a feedback loop between:
UTC[2] (status of the GPS A=available V=not available),
LATD/LOND (GPS position in decimal degrees), and
IMU_LAT/IMU_LON (IMU position in decimal degrees)
The idea would be that if UTC[2] was "A" the logic would equally average IMU_LAT/IMU_LON and LATD/LOND but if UTC[2] was "V" it would only calculate the position based on the last position recorded and IMU_north/IMU_east (offsets based on heading and acceleration values.
import time, inspect
import IMU
import serial
import datetime
import os
import math
import logging
log = open(time.strftime("%Y%m%d-%H%M%S")+'_GSPData.csv','w')
#f.write("UTC TIME,NAVSTATUS,LAT,LON,HDG,SPD,X,Y,Z")
RAD_TO_DEG = 57.29578
M_PI = 3.14159265358979323846
G_GAIN = 0.070 # [deg/s/LSB] If you change the dps for gyro, you need to update this value accordingly
AA = 0.40 # Complementary filter constant
magXmin = 0
magYmin = 0
magZmin = 0
magXmax = 0
magYmax = 0
magZmax = 0
gyroXangle = 0.0
gyroYangle = 0.0
gyroZangle = 0.0
CFangleX = 0.0
CFangleY = 0.0
IMU.detectIMU() #Detect if BerryIMUv1 or BerryIMUv2 is connected.
IMU.initIMU() #Initialise the accelerometer, gyroscope and compass
a = datetime.datetime.now()
ser = serial.Serial('/dev/serial0', 9600)
def truncate(n, decimals=0):
multiplier = 10 ** decimals
return int(n * multiplier) / multiplier
log.write("UTC,NAVSTAT,LAT,LON,HDG,SPD,xm/s,ym/s,zm/s")
log.write("\n")
try:
while True:
#Read the GPS, accelerometer, gyroscope and magnetometer values
NMEA = ser.readline()
NMEA_str_data = NMEA.decode('utf-8')
NMEA_data_arr=NMEA_str_data.split()
UTC = NMEA_str_data.split(',')
GYRx = IMU.readGYRx()
GYRy = IMU.readGYRy()
GYRz = IMU.readGYRz()
ACCx = IMU.readACCx()
ACCy = IMU.readACCy()
ACCz = IMU.readACCz()
#output the values of the accelerometer in m/s
yG = ((ACCx * 0.244)/1000)*9.80665
xG = ((ACCy * 0.244)/1000)*9.80665
zG = ((ACCz * 0.244)/1000)*9.80665
MAGx = IMU.readMAGx()
MAGy = IMU.readMAGy()
MAGz = IMU.readMAGz()
#Apply compass calibration
MAGx -= (magXmin + magXmax) /2
MAGy -= (magYmin + magYmax) /2
MAGz -= (magZmin + magZmax) /2
##Calculate loop Period(LP). How long between Gyro Reads
b = datetime.datetime.now() - a
a = datetime.datetime.now()
LP = b.microseconds/(1000000*1.0)
outputString = "Loop Time %5.2f " % ( LP )
#Convert Gyro raw to degrees per second
rate_gyr_x = GYRx * G_GAIN
rate_gyr_y = GYRy * G_GAIN
rate_gyr_z = GYRz * G_GAIN
#Calculate the angles from the gyro.
gyroXangle+=rate_gyr_x*LP
gyroYangle+=rate_gyr_y*LP
gyroZangle+=rate_gyr_z*LP
#Convert Accelerometer values to degrees
AccXangle = (math.atan2(ACCy,ACCz)*RAD_TO_DEG)
AccYangle = (math.atan2(ACCz,ACCx)+M_PI)*RAD_TO_DEG
#convert the values to -180 and +180
if AccYangle > 90:
AccYangle -= 270.0
else:
AccYangle += 90.0
#Complementary filter used to combine the accelerometer and gyro values.
CFangleX=AA*(CFangleX+rate_gyr_x*LP) +(1 - AA) * AccXangle
CFangleY=AA*(CFangleY+rate_gyr_y*LP) +(1 - AA) * AccYangle
#Calculate heading
heading = 180 * math.atan2(MAGy,MAGx)/M_PI
#Only have our heading between 0 and 360
if heading < 0:
heading += 360
####################################################################
###################Tilt compensated heading#########################
####################################################################
#Normalize accelerometer raw values.
accXnorm = ACCx/math.sqrt(ACCx * ACCx + ACCy * ACCy + ACCz * ACCz)
accYnorm = ACCy/math.sqrt(ACCx * ACCx + ACCy * ACCy + ACCz * ACCz)
accZnorm = ACCz/math.sqrt(ACCx * ACCx + ACCy * ACCy + ACCz * ACCz)
Zms_norm = zG-9.80665
Yms_norm = yG
Xms_norm = xG
#Calculate course
Course = (180*math.atan2(Xms_norm,Yms_norm)/M_PI)
#Only have our course between 0 and 360
if Course < 0:
Course +=360
#Calculate pitch and roll
pitch = math.asin(accXnorm)
roll = -math.asin(accYnorm/math.cos(pitch))
#Calculate the new tilt compensated values
magXcomp = MAGx*math.cos(pitch)+MAGz*math.sin(pitch)
#The compass and accelerometer are orientated differently on the LSM9DS0 and LSM9DS1 and the Z axis on the compass
#is also reversed. This needs to be taken into consideration when performing the calculations
if(IMU.LSM9DS0):
magYcomp = MAGx*math.sin(roll)*math.sin(pitch)+MAGy*math.cos(roll)-MAGz*math.sin(roll)*math.cos(pitch) #LSM9DS0
else:
magYcomp = MAGx*math.sin(roll)*math.sin(pitch)+MAGy*math.cos(roll)+MAGz*math.sin(roll)*math.cos(pitch) #LSM9DS1
#Calculate tilt compensated heading
tiltCompensatedHeading = 180 * math.atan2(magYcomp,magXcomp)/M_PI
if tiltCompensatedHeading < 0:
tiltCompensatedHeading += 360
#convert IMU readings to northings and eastings
IMU_north= (math.cos(tiltCompensatedHeading))*(Yms_norm+Xms_norm)
IMU_east= (math.sin(tiltCompensatedHeading))*(Yms_norm+Xms_norm)
#convert IMU_north to D.D
IMU_north_D= IMU_north/110723.41272
#Convert IMU_east to d.d
IMU_east_D= IMU_east/103616.02936
############################ END ##################################
#"%am/s": no rounding "%bm/s": unsupported "%cm/s": unsupported
#"%dm/s": whole numbers "%em/s": scientific notation "%fm/s": six digits
#"%gm/s": five digits
if NMEA_str_data.startswith('$GNRMC'):
if UTC[2] =="V":
#print("GPS unavaliable","heading",round(tiltCompensatedHeading,2),",course",round(Course,2),xG,yG,zG,"IMU_LAT","IMU_LON")
print("UTC",truncate(float(UTC[1]),0),",IMU",",LAT",",LON",",heading",round(tiltCompensatedHeading,2),",course",round(Course,2),truncate(IMU_north,4),truncate(IMU_east,4))
#log the output GPS invalid
log.write(UTC[1]+','+UTC[2]+','+""+','""+','+str(round(tiltCompensatedHeading,2))+','+UTC[7]+','+str(IMU_north)+','+str(IMU_east))
else:
#convert UTC from DDMM.MMM to DD.DDDD
if UTC[4] =="N":
LATD= (truncate(float(UTC[3]),-2)/100)+((float(UTC[3])-(truncate(float(UTC[3]),-2)))/60)
else:
LATD= -(truncate(float(UTC[3]),-2)/100)+((float(UTC[3])-(truncate(float(UTC[3]),-2)))/60)
if UTC[6] =="E":
LOND= (truncate(float(UTC[5]),-2)/100)+((float(UTC[5])-(truncate(float(UTC[5]),-2)))/60)
else:
LOND= -(truncate(float(UTC[5]),-2)/100)+((float(UTC[5])-(truncate(float(UTC[5]),-2)))/60)
#calculate IMU_LAT
IMU_LAT= LATD+IMU_north_D
#Calculate IMU_LON
IMU_LON= LOND+IMU_east_D
#write the output
print("UTC",truncate(float(UTC[1]),0),",GPS",",LAT",truncate(LATD,5),truncate(IMU_LAT,5),",LON",truncate(LOND,5),truncate(IMU_LON,5),",heading",round(tiltCompensatedHeading,2),",course",round(Course,2),UTC[8],",speed",truncate(float(UTC[7]),2))
#log the output GPS valid
log.write(UTC[1]+','+UTC[2]+','+str(LATD)+','+str(LOND)+','+str(round(tiltCompensatedHeading,2))+','+UTC[7]+','+str(IMU_north)+','+str(IMU_east))
log.write("\n")
#slow program down a bit, makes the output more readable
time.sleep(0.5)
#print(" aX = %fG aY =%fG aZ =%fG " % ( ACCx, ACCy, ACCz))
#slow program down a bit, makes the output more readable
#time.sleep(0.5)
except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
print ("Done.\nExiting.")
log.close()
Like I said I'm new and I'm sure you pros are going to tell me its really sloppy but I will gladly accept any constructive criticism.
Thanks, Troy

trouble reading file in same script after print export via sys.stdout to the same file

I am utilizing an audio analysis script that outputs a bunch of print commands. After the print commands are all executed and exported to an external text file via sys.stdout in the script, I want to read the text file in the same script. However, the text file comes out empty within the script. I have tried running f.flush() and f.seek() which doesn't help. When I open the text file after the script has completed, the contents of all the print commands display as expected. It looks like the script has to complete in order for sys.stdout() to write the contents to the external file. Can I ensure that this step happens prior to the f.read() at the end of my script?
import sys
sys.path.append(".")
#print(sys.path)
import time
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
start_time = time.time()
import numba
import numpy as np
import librosa
def print_color(msg, color=32):
if sys.stdout.isatty():
print("\033[{color}m {msg} \033[0m".format(msg=msg, color=color))
else:
print(" *** {msg} ***".format(msg=msg))
# TODO: configure this via cmdline
SAMPLE_RATE = 44100 # Hz
ELF_THRESHOLD_DB = -22 #dB
#OLDBUCKET N_FFT = 16384
N_FFT = 13000
FIRST_BASS_BUCKET = 0
LAST_BASS_BUCKET = 11
LAST_ANALYSIS_BUCKET = 64
DEBUG_ENABLED = False
if len(sys.argv) < 2:
print("ElfTag: Extremely Low Frequency Audio Tagger")
print("Usage: %(cmd)s </path/to/directory>" % {"cmd": sys.argv[0]})
sys.exit(0)
#filename = sys.argv[1]
directory = sys.argv[1]
print(directory)
#filename = librosa.util.example_audio_file()
#filename = "/Volumes/SDXC128GB/ElfTag/sp/02. Luma.mp3"
def debug(msg):
if DEBUG_ENABLED:
print(msg)
files = librosa.util.find_files(directory)
print("Total Tracks: ",len(files))
queue = (len(files))
class Tee:
def write(self, *args, **kwargs):
self.out1.write(*args, **kwargs)
self.out2.write(*args, **kwargs)
def __init__(self, out1, out2):
self.out1 = out1
self.out2 = out2
def flush(self):
pass
import sys
logfile = input ("Enter Filename for Log File: ")
sys.stdout = Tee(open(logfile, "w"), sys.stdout)
for filename in files:
try:
queue = queue - 1
print(queue, "Songs Remaining")
print("Loading %(filename)s" % {"filename": filename})
y, sr = librosa.load(filename, sr=None)
duration = librosa.core.get_duration(y=y, sr=sr)
print("Detected sample rate: %(sr)d Hz, duration: %(duration)f seconds." % {"sr": sr, "duration": duration})
bin_size_hz = float(sr) / N_FFT
num_bins = N_FFT / 2 + 1
print("Using transform length of %(n_fft)d for FFT, which gives us %(num_bins)d bins at %(bin_size_hz)f Hz per bin." % {"n_fft": N_FFT, "num_bins": num_bins, "bin_size_hz": bin_size_hz})
start_hz = bin_size_hz * FIRST_BASS_BUCKET
end_hz = bin_size_hz * (LAST_BASS_BUCKET + 1)
anal_hz = bin_size_hz * (LAST_ANALYSIS_BUCKET + 1)
print("Detecting deep bass as peaks between %(start)f Hz and %(end)f Hz above %(db)d dB chosen from frequency range below %(anal)f Hz." % { "start" : start_hz, "end" : end_hz, "db" : ELF_THRESHOLD_DB, "anal" : anal_hz })
#y = librosa.core.to_mono(y)
D = librosa.stft(y, n_fft = N_FFT)
tempo, beats = librosa.beat.beat_track(y=y, sr=sr, units='frames', hop_length=512)
numBeats = beats.shape[0]
print("Estimated tempo: %(tempo)f." % {"tempo" : tempo})
print("Number of beats detected: %(beats)d." % {"beats" : numBeats})
# Split into Harmonic and Percussive layers to aid with beat detection
#H, P = librosa.decompose.hpss(D)
P = D
P = librosa.amplitude_to_db(P, ref=np.max)
totalFrames = P.shape[1]
print("Total frames: %(frames)d, about %(secPerFrame)f seconds per frame" % {"frames": totalFrames, "secPerFrame": (duration / totalFrames)})
# Select significant bass frame rows
Pbass = P[FIRST_BASS_BUCKET:(LAST_ANALYSIS_BUCKET + 1)]
firstFrame = np.argmax(Pbass.max(axis=0) > -80)
debug("firstFrame")
debug(firstFrame)
Pbass = Pbass[:, firstFrame:]
debug("Pbass")
debug(Pbass)
localmaxBass = librosa.util.localmax(Pbass)
debug("localmaxBass")
debug(localmaxBass)
maskBass = localmaxBass[FIRST_BASS_BUCKET:(LAST_BASS_BUCKET + 1)]
debug("maskBass")
debug(maskBass)
ourBass = Pbass[FIRST_BASS_BUCKET:(LAST_BASS_BUCKET + 1)]
debug("ourBass")
debug(ourBass)
filteredBass = (ourBass > ELF_THRESHOLD_DB)
debug("filteredBass")
debug(filteredBass)
peakFilteredBass = np.multiply(filteredBass, maskBass)
debug("peakFilteredBass")
debug(peakFilteredBass)
vertBassFrames = np.sum(filteredBass, axis=0)
debug("vertBassFrames")
debug(vertBassFrames)
horizBassFrames = (vertBassFrames > 0)
debug("horizBassFrames")
debug(horizBassFrames)
deepBassFrames = np.nonzero(horizBassFrames)[0]
debug("deepBassFrames")
debug(deepBassFrames.shape)
debug(deepBassFrames)
# Adjacent Deep Bass detector
shiftedHorizBassFrames = np.append(horizBassFrames[1:], [False])
andedShiftedHorizBassFrames = np.logical_and(horizBassFrames, shiftedHorizBassFrames)
adjacentHorizBassFrames = np.logical_and(andedShiftedHorizBassFrames, np.append(andedShiftedHorizBassFrames[1:], [False]))
debug("adjacentHorizBassFrames")
debug(adjacentHorizBassFrames)
# /End Adjacent Deep Bass detector
debug("beats")
debug(beats.shape)
debug(beats)
deepBassBeats = np.intersect1d(deepBassFrames, beats, assume_unique=True)
debug("deepBassBeats")
debug(deepBassBeats.shape)
debug(deepBassBeats)
numDeepBeats = deepBassBeats.shape[0]
print("Number of deep beats: %(numDeepBeats)d" % {"numDeepBeats": numDeepBeats})
deepBeatsPercentage = float(numDeepBeats) / numBeats
print("Percentage of deep beats: %(deepBeatsPercentage)f" % {"deepBeatsPercentage": deepBeatsPercentage})
numBassFrames = horizBassFrames.sum()
print("Number of frames with deep bass: %(frames)d." % {"frames": numBassFrames})
numAdjacentBassFrames = adjacentHorizBassFrames.sum()
print("Number of adjacent frames with deep bass: %(frames)d." % {"frames": numAdjacentBassFrames})
bassFramesPerBeat = float(numBassFrames) / numBeats
print("Number of deep bass frames per beat: %(bassFramesPerBeat)f" % {"bassFramesPerBeat": bassFramesPerBeat})
bassFramesPercentage = float(numBassFrames) / totalFrames
print("Percentage of deep bass frames: %(bassFramesPercentage)f" % {"bassFramesPercentage": bassFramesPercentage})
adjacentBassFramesPercentage = float(numAdjacentBassFrames) / totalFrames
print("Percentage of adjacent deep bass frames: %(bassFramesPercentage)f" % {"bassFramesPercentage": adjacentBassFramesPercentage})
#if %(bassFramesPercentage)f" % {"bassFramesPercentage": adjacentBassFramesPercentage} >= "0.30":
# print("DEEP BASS TRACK NEEDS TAGGING")
print(("--- %s seconds ---" % (time.time() - start_time)))
#sys.exit(0)
except:
continue
f= open(logfile, 'r+')
f.flush()
f.seek(0)
fh = f.read()
print(fh.rstrip())
You're not flushing the output file that you're writing to. Implement the flush method in your Tee class:
def flush(self):
self.out1.flush()
self.out2.flush()
Then use sys.stdout.flush() at the end of your loop.

(Almost Done) Python program that closes stream when below audio threshold

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

Detect tap with pyaudio from live mic

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

Categories

Resources