-
Notifications
You must be signed in to change notification settings - Fork 0
/
wavetrigger.py
112 lines (80 loc) · 3.56 KB
/
wavetrigger.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
"""
@copyright: 2013 Single D Software - All Rights Reserved
@summary: Provides the HTTP server for Light Maestro.
"""
# Standard library imports
import collections
import logging
import os
import struct
import threading
import time
# Additional library imports
import requests
# Named logger for this module
_logger = logging.getLogger(__name__)
# Configure requests to not log so much
logging.getLogger('requests.packages.urllib3').setLevel(logging.WARNING)
# We use a session variable so that HTTP keep-alive is utilized, and
# also so we'll always remember to set the content type appropriately.
_session = requests.session()
_session.headers['Content-Type'] = 'application/json'
# Stores previous last access times for each file
# so they can be compared each time files are polled.
_atimes = collections.defaultdict(time.time)
def _triggerpoller(host):
"""TODO"""
# Poll the list of files forever
while True:
# Delay the appropriate amount of time between polls
time.sleep(0.25)
# Grab a list of all fully-qualified wave file names in the trigger folder
files = (os.path.join('triggers', f) for f in os.listdir('triggers') if os.path.splitext(f)[1] == '.wav')
# Iterate over the list of files
for filename in files:
# If the last access time is newer than what was previous recorded then take
# action on that file. A small threshold is used to prevent "double bouncing".
if os.stat(filename).st_atime - _atimes[filename] > 1.0:
# Open the file and pull out the data
with open(filename, 'rb') as f:
req = f.read()
# Immediately store off the last accessed time
_atimes[filename] = os.stat(filename).st_atime
# Separate the components of the request
reqitems = req[52:].splitlines(False)
method = reqitems[0].decode()
url = 'http://{0}:3520{1}'.format(host, reqitems[1].decode())
try:
data = reqitems[2].decode()
except IndexError:
data = ''
# Attempt to send the request and log the results
_logger.debug('Sending {0} request to {1}'.format(method, url))
try:
response = _session.request(method, url, data=data)
_logger.debug('Received response with status code {0}'.format(response.status_code))
except requests.RequestException:
_logger.warning('Unable to contact {0}'.format(url))
def writewave(method, url, name, data):
"""TODO"""
req_data = '\n'.join((method, url, data))
req_len = len(req_data)
if req_len % 2 == 1:
req_data += '\n'
req_len += 1
file_len = 36 + 8 + req_len
riff_chunk = struct.pack('<4sL4s', 'RIFF'.encode(), file_len, 'WAVE'.encode())
fmt_chunk = struct.pack('<4sL2H2L2H', 'fmt '.encode(), 16, 1, 1, 22050, 44100, 2, 16)
data_chunk = struct.pack('<4sL', 'data'.encode(), 0)
req_chunk = struct.pack('<4sL', 'req '.encode(), req_len) + req_data.encode()
filename = getwavefilename(name)
with open(filename, 'wb') as f:
f.write(riff_chunk + fmt_chunk + data_chunk + req_chunk)
def writewavefile(sceneid):
"""TODO"""
writewave('POST', '/scenes/{0}/_change'.format(sceneid), sceneid, '')
def getwavefilename(sceneid):
return 'triggers/' + sceneid + '.wav'
def start(host):
"""Initializes the module."""
threading.Thread(target=_triggerpoller, args=[host]).start()