Skip to content

Commit 054b77c

Browse files
committed
Initial sketch.
1 parent 2d3ee31 commit 054b77c

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

asr/__init__.py

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
from email.mime.multipart import MIMEMultipart
2+
from email.message import Message
3+
import json
4+
import struct
5+
6+
from flask import Flask, request, Response
7+
from google.cloud import speech
8+
9+
10+
app = Flask(__name__)
11+
12+
13+
# We know gunicorn does this, but it doesn't *say* it does this, so we must signal it manually.
14+
@app.before_request
15+
def handle_chunking():
16+
request.environ['wsgi.input_terminated'] = 1
17+
18+
19+
def parse_chunks(stream):
20+
boundary = b'--' + request.headers['content-type'].split(';')[1].split('=')[1].encode('utf-8').strip() # super lazy/brittle parsing.
21+
print("Boundary: " + boundary.decode('utf-8'))
22+
this_frame = b''
23+
while True:
24+
content = stream.read(4096)
25+
this_frame += content
26+
end = this_frame.find(boundary)
27+
if end > -1:
28+
frame = this_frame[:end]
29+
if frame != b'':
30+
header, content = frame.split(b'\r\n\r\n', 1)
31+
print(content)
32+
yield content[:-2]
33+
this_frame = this_frame[end + len(boundary):]
34+
if content == b'':
35+
print("End of input.")
36+
break
37+
38+
39+
def parse_data():
40+
boundary = b'--' + request.headers['content-type'].split(';')[1].split('=')[1].encode('utf-8').strip() # super lazy/brittle parsing.
41+
parts = request.data.split(boundary)
42+
for part in parts:
43+
if part == b'':
44+
continue
45+
yield part.split(b'\r\n\r\n', 1)[1][:-2]
46+
47+
48+
@app.route('/NmspServlet/', methods=["POST"])
49+
def recognise():
50+
51+
client = speech.SpeechClient()
52+
stream = request.stream
53+
chunks = iter(list(parse_chunks(stream)))
54+
content = next(chunks).decode('utf-8')
55+
print(content)
56+
57+
config = speech.types.RecognitionConfig(
58+
encoding='SPEEX_WITH_HEADER_BYTE',
59+
language_code='en-US',
60+
sample_rate_hertz=16000,
61+
)
62+
print('beginning request')
63+
responses = client.streaming_recognize(
64+
config=speech.types.StreamingRecognitionConfig(config=config),
65+
requests=(
66+
speech.types.StreamingRecognizeRequest(audio_content=struct.pack('B', len(x)) + x)
67+
for x in chunks))
68+
print('finished request')
69+
words = []
70+
for response in responses:
71+
if response.results:
72+
for result in response.results:
73+
words.extend({
74+
'word': x,
75+
'confidence': result.alternatives[0].confidence
76+
} for x in result.alternatives[0].transcript.split(' '))
77+
78+
# Now for some reason we also need to give back a mime/multipart message...
79+
parts = MIMEMultipart()
80+
response_part = Message()
81+
response_part.add_header('Content-Type', 'application/JSON; charset=utf-8')
82+
83+
if len(words) > 0:
84+
response_part.add_header('Content-Disposition', 'form-data; name="QueryResult"')
85+
words[0]['word'] += '\\*no-space-before'
86+
response_part.set_payload(json.dumps({
87+
'words': [words],
88+
}))
89+
else:
90+
response_part.add_header('Content-Disposition', 'form-data; name="QueryRetry"')
91+
# Other errors probably exist, but I don't know what they are.
92+
# This is a Nuance error verbatim.
93+
response_part.set_payload(json.dumps({
94+
"Cause": 1,
95+
"Name": "AUDIO_INFO",
96+
"Prompt": "Sorry, speech not recognized. Please try again."
97+
}))
98+
parts.attach(response_part)
99+
print(parts.as_string())
100+
101+
response = Response(parts.as_string().split("\n", 3)[3])
102+
response.headers['Content-Type'] = f'multipart/form-data; boundary={parts.get_boundary()}'
103+
response.headers['Connection'] = 'close'
104+
return response
105+

0 commit comments

Comments
 (0)