Skip to content

Commit 2ec0a17

Browse files
committed
Fixed two TimingStream bugs, tests
* It was passing an absolute time to setTimeout rather than an offset * It could sometimes fail to fire the end and close events if the source was too slow
1 parent 725873f commit 2ec0a17

File tree

6 files changed

+364
-13
lines changed

6 files changed

+364
-13
lines changed

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ IBM Watson Speech Services for Web Browsers
66

77
Allows you to easily add voice recognition and synthesis to any web app with minimal code.
88

9-
**Warning** This library is still early-stage and may see significant breaking changes.
9+
**Warning** This library is still has a few rough edges and may yet see breaking changes.
1010

1111
**For Web Browsers Only** This library is primarily intended for use in browsers.
1212
Check out [watson-developer-cloud](https://www.npmjs.com/package/watson-developer-cloud) to use Watson services (speech and others) from Node.js.
@@ -139,6 +139,7 @@ Accepts input from `RecognizeStream()` and friends, writes text to supplied `out
139139
### v0.15
140140
* Removed `SpeechToText.recognizeElement()` due to quality issues
141141
* Added `options.element` to TextToSpeech.synthesize() to support playing through exiting elements
142+
* Fixed a couple of bugs in the TimingStream
142143

143144
### v0.14
144145
* Moved getUserMedia shim to a [standalone library](https://www.npmjs.com/package/get-user-media-promise)
@@ -184,17 +185,14 @@ Accepts input from `RecognizeStream()` and friends, writes text to supplied `out
184185

185186
* Solidify API
186187
* break components into standalone npm modules where it makes sense
187-
* record which shim/pollyfills would be useful to extend partial support to older browsers (Promise, fetch, etc.)
188188
* run integration tests on travis (fall back to offline server for pull requests)
189189
* more tests in general
190-
* better cross-browser testing (saucelabs?)
190+
* better cross-browser testing (IE, Safari, mobile browsers - maybe saucelabs?)
191191
* update node-sdk to use current version of this lib's RecognizeStream (and also provide the FormatStream + anything else that might be handy)
192192
* move `result` and `results` events to node wrapper (along with the deprecation notice)
193193
* improve docs
194194
* consider a wrapper to match https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
195195
* support a "hard" stop that prevents any further data events, even for already uploaded audio, ensure timing stream also implements this.
196-
* handle pause/resume in media element streams - perhaps just stop and then create a new stream on resume, using the same token
197-
* consider moving STT core to standalone module
198196
* look for bug where single-word final results may omit word confidence (possibly due to FormatStream?)
199197
* fix bug where TimingStream shows words slightly before they're spoken
200198
* support jquery objects for element and targetElement

dist/watson-speech.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"karma-mocha": "^0.2.2",
3535
"mocha": "^2.4.5",
3636
"serve-static": "^1.10.2",
37+
"sinon": "^1.17.3",
3738
"uglify-js": "^2.6.2",
3839
"watchify": "^3.7.0",
3940
"watson-developer-cloud": "^1.4.1"

speech-to-text/timing-stream.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var defaults = require('defaults');
1010
*
1111
* Useful when running recognizeFile because the text can otherwise appear before the words are spoken
1212
*
13-
* @param {Object} opts
13+
* @param {Object} [opts]
1414
* @param {*} [opts.emitAtt=TimingStream.START] - set to TimingStream.END to only emit text that has been completely spoken.
1515
* @param {Number} [opts.delay=0] - Additional delay (in seconds) to apply before emitting words, useful for precise syncing to audio tracks. May be negative
1616
* @constructor
@@ -35,6 +35,7 @@ function TimingStream(opts) {
3535
this.on('pipe', function(source) {
3636
source.on('end', function() {
3737
self.sourceEnded = true; // todo: see if there's anything built-in that does this for us
38+
self.checkForEnd();
3839
});
3940
});
4041
}
@@ -176,17 +177,28 @@ TimingStream.prototype.scheduleNextTick = function scheduleNextTick(cutoff) {
176177
for (var i = 0; i < timestamps.length; i++) {
177178
var wordOffset = timestamps[i][this.options.emitAt];
178179
if (wordOffset > cutoff) {
179-
this.nextTick = setTimeout(this.tick.bind(this), this.startTime + (wordOffset * 1000));
180+
var nextTime = this.startTime + (wordOffset * 1000);
181+
this.nextTick = setTimeout(this.tick.bind(this), nextTime - Date.now());
180182
return;
181183
}
182184
}
183185
throw new Error('No future words found'); // this shouldn't happen ever - getCurrentResult should automatically delete the result from the buffer if all of it's words are consumed
184186
} else {
185-
// if we have no next result in the buffer, and the source has ended, then we're done.
186-
if (this.sourceEnded) {
187-
this.emit('close');
188-
this.push(null);
189-
}
187+
// clear the next tick
188+
this.nextTick = null;
189+
this.checkForEnd();
190+
}
191+
};
192+
193+
/**
194+
* Triggers the 'close' and 'end' events if both pre-conditions are true:
195+
* - the previous stream must have already emitted it's 'end' event
196+
* - there must be no next tick scheduled, indicating that there are no results buffered for later delivery
197+
*/
198+
TimingStream.prototype.checkForEnd = function() {
199+
if (this.sourceEnded && !this.nextTick) {
200+
this.emit('close');
201+
this.push(null);
190202
}
191203
};
192204

test/resources/results.json

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
{
2+
"results": [
3+
{
4+
"word_alternatives": [
5+
{
6+
"start_time": 1.48,
7+
"alternatives": [
8+
{
9+
"confidence": 1,
10+
"word": "thunderstorms"
11+
}
12+
],
13+
"end_time": 2.33
14+
},
15+
{
16+
"start_time": 2.33,
17+
"alternatives": [
18+
{
19+
"confidence": 1,
20+
"word": "could"
21+
}
22+
],
23+
"end_time": 2.54
24+
},
25+
{
26+
"start_time": 2.54,
27+
"alternatives": [
28+
{
29+
"confidence": 1,
30+
"word": "produce"
31+
}
32+
],
33+
"end_time": 2.99
34+
},
35+
{
36+
"start_time": 2.99,
37+
"alternatives": [
38+
{
39+
"confidence": 1,
40+
"word": "large"
41+
}
42+
],
43+
"end_time": 3.35
44+
},
45+
{
46+
"start_time": 3.35,
47+
"alternatives": [
48+
{
49+
"confidence": 1,
50+
"word": "hail"
51+
}
52+
],
53+
"end_time": 3.82
54+
},
55+
{
56+
"start_time": 3.85,
57+
"alternatives": [
58+
{
59+
"confidence": 1,
60+
"word": "isolated"
61+
}
62+
],
63+
"end_time": 4.42
64+
},
65+
{
66+
"start_time": 4.42,
67+
"alternatives": [
68+
{
69+
"confidence": 1,
70+
"word": "tornadoes"
71+
}
72+
],
73+
"end_time": 5.04
74+
},
75+
{
76+
"start_time": 5.04,
77+
"alternatives": [
78+
{
79+
"confidence": 1,
80+
"word": "and"
81+
}
82+
],
83+
"end_time": 5.26
84+
},
85+
{
86+
"start_time": 5.26,
87+
"alternatives": [
88+
{
89+
"confidence": 1,
90+
"word": "heavy"
91+
}
92+
],
93+
"end_time": 5.58
94+
},
95+
{
96+
"start_time": 5.58,
97+
"alternatives": [
98+
{
99+
"confidence": 1,
100+
"word": "rain"
101+
}
102+
],
103+
"end_time": 6.14
104+
}
105+
],
106+
"alternatives": [
107+
{
108+
"word_confidence": [
109+
[
110+
"thunderstorms",
111+
1
112+
],
113+
[
114+
"could",
115+
1
116+
],
117+
[
118+
"produce",
119+
1
120+
],
121+
[
122+
"large",
123+
1
124+
],
125+
[
126+
"hail",
127+
1
128+
],
129+
[
130+
"isolated",
131+
1
132+
],
133+
[
134+
"tornadoes",
135+
1
136+
],
137+
[
138+
"and",
139+
1
140+
],
141+
[
142+
"heavy",
143+
1
144+
],
145+
[
146+
"rain",
147+
0.9904317537290094
148+
]
149+
],
150+
"confidence": 0.999,
151+
"transcript": "thunderstorms could produce large hail isolated tornadoes and heavy rain ",
152+
"timestamps": [
153+
[
154+
"thunderstorms",
155+
1.48,
156+
2.33
157+
],
158+
[
159+
"could",
160+
2.33,
161+
2.54
162+
],
163+
[
164+
"produce",
165+
2.54,
166+
2.99
167+
],
168+
[
169+
"large",
170+
2.99,
171+
3.35
172+
],
173+
[
174+
"hail",
175+
3.35,
176+
3.82
177+
],
178+
[
179+
"isolated",
180+
3.85,
181+
4.42
182+
],
183+
[
184+
"tornadoes",
185+
4.42,
186+
5.04
187+
],
188+
[
189+
"and",
190+
5.04,
191+
5.26
192+
],
193+
[
194+
"heavy",
195+
5.26,
196+
5.58
197+
],
198+
[
199+
"rain",
200+
5.58,
201+
6.14
202+
]
203+
]
204+
}
205+
],
206+
"final": true
207+
}
208+
],
209+
"result_index": 0
210+
}

0 commit comments

Comments
 (0)