-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
307 lines (251 loc) · 11.1 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#importing libraries to run script
import pyaudio
import math
import struct
import wave
import time
import os
import sys
import argparse
import logging
import boto3
from botocore.exceptions import ClientError
import pathlib as path
import urllib.request
import threading
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--path", help="Enter the desired directory", type=str)
parser.add_argument("-f", "--filename", help="Enter the desired file name", type=str)
parser.add_argument("-d", "--device_name", help="Enter the desired device name", type=str)
parser.add_argument("-s", "--stream_id", help="Enter the desired stream id", type=str)
parser.add_argument("-a", "--aws_profile", help="Enter the desired aws profile", type=str)
parser.add_argument("-r", "--rate", help="Enter the desired rate", type=int)
args = parser.parse_args()
directory = args.path if args.path else r'\tmp'
file_name = args.filename if args.filename else ''
device_name = args.device_name if args.device_name else ''
stream_id = args.stream_id if args.stream_id else ''
aws_profile = args.aws_profile if args.aws_profile else ''
input_rate = args.rate if args.rate else ''
directory.encode('unicode_escape')
file_name.encode('unicode_escape')
device_name.encode('unicode_escape')
stream_id.encode('unicode_escape')
aws_profile.encode('unicode_escape')
input_rate.encode('unicode_escape')
#this is where your audio files will be stored. Please change this to meet your liking.
#directory = r'C:\Users\dunka\Documents\GitHub\audioproject\directory'
#Some variables to work with
FORMAT = pyaudio.paInt16
#customize number of channels
CHANNELS = 1
SHORT_NORMALIZE = (1.0/32768.0)
#logic to check for validity of environ variables. if not met, results in defaults
if(os.getenv('chunk') == None or isinstance(os.getenv('chunk'), int)):
chunk = 1024
else:
chunk = int(os.getenv('chunk'))
if(os.getenv('RATE') == None or isinstance(os.getenv('RATE'), int)):
if input_rate:
RATE = input_rate
RATE = 16000
else:
RATE = int(os.getenv('RATE'))
if(os.getenv('directory') == None or isinstance(os.getenv('directory'), int)):
directory = directory
else:
directory = os.getenv('directory')
if(not os.path.isdir(directory)):
sys.exit('Specified directory does not exist!')
else:
if os.access(directory, os.W_OK) is not True:
sys.exit('Specified directory is not writable!')
if(os.getenv('stream_id') == None or isinstance(os.getenv('stream_id'), int)):
stream_id = stream_id
else:
stream_id = os.getenv('stream_id')
if device_name == '':
if(os.getenv('device_name') == None or isinstance(os.getenv('device_name'), int)):
sys.exit('Device Name Not Found! Please specify device name.')
else:
device_name = os.getenv('device_name')
if stream_id == '':
if(os.getenv('stream_id') == None or isinstance(os.getenv('stream_id'), int)):
sys.exit('Stream ID Not Found! Please specify stream id.')
else:
stream_id = os.getenv('stream_id')
if aws_profile == '':
try:
aws_profile = os.getenv('aws_profile')
except:
pass
swidth = 2
#customize this value based on background noise. The higher the value, the louder the sound must be to be considered a sound.
maxSoundVal = 10
#Interval AFTER sound finishes that script waits. Don't make this zero.
pauseInterval = 1
#Amazon S3 Bucket to store recordings
bucketName = 'python-recording-bucket'
WAIT_SECONDS = 60
class SoundRecorder:
#calculates current audio level
@staticmethod
def calculator(audio):
vals = len(audio) / swidth
calc_format = "%dh" % (vals)
shorts_ex = struct.unpack(calc_format, audio)
counter = 0.0
for sample in shorts_ex:
n = sample * SHORT_NORMALIZE
counter += n * n
calculator = math.pow(counter / vals, 0.5)
return calculator * 1000
def __init__(self):
self.p = pyaudio.PyAudio()
info = self.p.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
global device_name
global device_id
device_id = 0
for i in range(0, numdevices):
if (self.p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
print("Input Device ID:", i, " - ", self.p.get_device_info_by_host_api_device_index(0, i).get('name'))
for i in range(0, numdevices):
audio_names = []
if (self.p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
if(self.p.get_device_info_by_host_api_device_index(0, i).get('name') == device_name):
device_id = i
if device_id == 0:
sys.exit("Audio device not found! Please modify device_name variable.")
self.stream = self.p.open(format=FORMAT,
input_device_index=device_id,
channels=CHANNELS,
rate=RATE,
input=True,
output=True,
frames_per_buffer=chunk)
def upload_file(self, file_name, bucket, object_name):
"""Upload a file to an S3 bucket
:param file_name: File to upload
:param bucket: Bucket to upload to
:param object_name: S3 object name. If not specified then file_name is used
:return: True if file was uploaded, else False
"""
sts = boto3.client('sts')
if not sts.get_caller_identity():
sys.exit("Improper AWS credentials entered!")
# If S3 object_name was not specified, use file_name
if object_name is None:
object_name = file_name
# Upload the file
if aws_profile:
try:
session = boto3.Session(profile_name=aws_profile)
s3_client = session.client('s3')
except:
print("Selected AWS Profile is not valid! Trying default profile.")
else:
try:
s3_client = boto3.client('s3')
except:
sys.exit("Default profile is not valid!")
try:
from datetime import date
global stream_id
file_prefix = stream_id + '/' + date.today().strftime('%Y/%m/%d')
response = s3_client.upload_file(file_name, bucket, '{}.mp3'.format(file_prefix + '/' + object_name), ExtraArgs={'ACL':'public-read'})
except ClientError as e:
pass
return True
#helper method to check for internet connection
def connect(self, host='http://google.com'):
try:
urllib.request.urlopen(host)
return True
except:
return False
#function responsible for listening to sound
def listenForSound(self):
print('Python listening script initiated!')
self.checkForRemaining(directory)
while True:
if not(self.p.get_device_info_by_host_api_device_index(0, device_id).get('name')):
sys.exit("Audio device disconnected. Please reconnect!")
input = self.stream.read(chunk, exception_on_overflow = False)
current_sound_level = self.calculator(input)
if current_sound_level > maxSoundVal:
start_time = int(time.time())
self.recordSound(start_time)
#function responsible for recording sound
def recordSound(self, start_time):
print('Noise sensed, recording is beginning. To stop the recording, stop noise.')
currentSoundLevel = []
current = time.time()
end = time.time() + pauseInterval
while current <= end:
data = self.stream.read(chunk, exception_on_overflow = False)
if self.calculator(data) >= maxSoundVal: end = time.time() + pauseInterval
current = time.time()
currentSoundLevel.append(data)
duration = start_time - int(time.time())
threading.Thread(target=self.save, args=(b''.join(currentSoundLevel), duration,), daemon=True).start()
print('Returning to listening')
#self.save(b''.join(currentSoundLevel), duration)
#function that checks if any files are still remaining in a directory. If it is, send it to s3.
def checkForRemaining(self, directory):
dirname = path.Path(directory).glob("*.mp3")
paths = []
print('Scanning for leftover files!')
for file in dirname:
paths.append(str(file))
if paths:
for file in paths:
try:
if '\\' in file:
if 'networkerror' in file.split("\\")[-1].replace('.mp3',''):
self.upload_file(file, bucketName, file.split("\\")[-1].replace('.mp3','').replace('networkerror',''))
os.remove(file)
print('Leftover submitted!')
if '/' in file:
if 'networkerror' in file.split("/")[-1].replace('.mp3',''):
self.upload_file(file, bucketName, file.split("/")[-1].replace('.mp3','').replace('networkerror',''))
os.remove(file)
print('Leftover submitted!')
except:
pass
x = threading.Timer(WAIT_SECONDS, self.checkForRemaining, [directory])
x.daemon = True
x.start()
#function responsible for uploading sound files
def save(self, recording, duration):
try:
global file_name
name_of_file = file_name
if name_of_file == '':
if self.connect():
name_of_file = str(int(time.time())) + '' + str(duration)
else:
name_of_file = 'networkerror' + str(int(time.time())) + '' + str(duration)
else:
if self.connect():
name_of_file = name_of_file + '-' + str(int(time.time())) + '-' + str(duration)
else:
name_of_file = 'networkerror' + name_of_file + '-' + str(int(time.time())) + '-' + str(duration)
filename = os.path.join(directory, '{}.mp3'.format(name_of_file))
wf = wave.open(filename, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(self.p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(recording)
if self.connect():
self.upload_file(filename, bucketName, name_of_file)
delete_name = name_of_file
name_of_file = file_name
wf.close()
if self.connect():
os.remove(filename)
print('Written to file: {}'.format(filename))
except:
pass
a = SoundRecorder()
a.listenForSound()