Skip to content

Commit 6a48472

Browse files
committed
format stream: change default hesitation value, add space to end of interim results
1 parent 6f35ba6 commit 6a48472

File tree

2 files changed

+33
-13
lines changed

2 files changed

+33
-13
lines changed

speech-to-text/format-stream.js

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ var defaults = require('defaults');
1616
*
1717
* @param {Object} opts
1818
* @param {String} opts.model - some models / languages need special handling
19-
* @param {String} [opts.hesitation='\u2026'] - what to put down for a "hesitation" event, defaults to an ellipsis (...)
19+
* @param {String} [opts.hesitation=''] - what to put down for a "hesitation" event, also consider \u2026 (ellipsis: ...)
2020
* @param {Boolean} [options.objectMode=false] - emit `result` objects instead of string Buffers for the `data` events.
2121
* @constructor
2222
*/
2323
function FormatStream(opts) {
2424
this.options = defaults(opts, {
2525
model: '', // some models should have all spaces removed
26-
hesitation: '\u2026', // ellipsis
26+
hesitation: '',
2727
decodeStrings: false // false = don't convert strings to buffers before passing to _write
2828
});
2929
Transform.call(this, this.options);
@@ -33,7 +33,7 @@ function FormatStream(opts) {
3333
}
3434
util.inherits(FormatStream, Transform);
3535

36-
var reHesitation = /%HESITATION/g; // when the service detects a "hesitation" pause, it literally puts the string "%HESITATION" into the transcription
36+
var reHesitation = /%HESITATION ?/g; // http://www.ibm.com/watson/developercloud/doc/speech-to-text/output.shtml#hesitation - D_ is handled below
3737
var reRepeatedCharacter = /([a-z])\1{2,}/ig; // detect the same character repeated three or more times and remove it
3838
var reDUnderscoreWords = /D_[^\s]+/g; // replace D_(anything)
3939

@@ -45,7 +45,7 @@ var reDUnderscoreWords = /D_[^\s]+/g; // replace D_(anything)
4545
*/
4646
FormatStream.prototype.clean = function clean(text) {
4747
// clean out "junk"
48-
text = text.replace(reHesitation, this.options.hesitation)
48+
text = text.replace(reHesitation, this.options.hesitation ? this.options.hesitation.trim() + ' ' : this.options.hesitation)
4949
.replace(reRepeatedCharacter, '')
5050
.replace(reDUnderscoreWords,'');
5151

@@ -54,7 +54,7 @@ FormatStream.prototype.clean = function clean(text) {
5454
text = text.replace(/ /g,'');
5555
}
5656

57-
return text.trim();
57+
return text.trim() + ' '; // we want exactly 1 space at the end
5858
};
5959

6060
/**
@@ -73,12 +73,13 @@ FormatStream.prototype.capitalize = function capitalize(text) {
7373
* @returns {string}
7474
*/
7575
FormatStream.prototype.period = function period(text) {
76+
text = text.trim();
7677
// don't put a period down if the clean stage remove all of the text
7778
if (!text) {
7879
return ' ';
7980
}
8081
// just add a space if the sentence ends in an ellipse
81-
if (this.options.hesitation && text.substr(-1) === this.options.hesitation) {
82+
if (text.substr(-1) === '\u2026') {
8283
return text + ' ';
8384
}
8485
return text + (this.isJaCn ? '。' : '. ');
@@ -119,17 +120,23 @@ FormatStream.prototype.formatString = function(str, isInterim) {
119120
FormatStream.prototype.formatResult = function formatResult(data) {
120121
data = clone(data);
121122
if (Array.isArray(data.results)) {
122-
data.results.forEach(function(result) {
123+
data.results.forEach(function(result, i) {
124+
125+
// if there are multiple interim results (as produced by the speaker stream),
126+
// treat the text as final in all but the last result
127+
var textFinal = result.final || (i !== (data.results.length - 1));
128+
123129
result.alternatives = result.alternatives.map(function(alt) {
124-
alt.transcript = this.formatString(alt.transcript, !result.final);
130+
alt.transcript = this.formatString(alt.transcript, !textFinal);
125131
if (alt.timestamps) {
126-
alt.timestamps = alt.timestamps.map(function(ts, i, arr) {
132+
alt.timestamps = alt.timestamps.map(function(ts, j, arr) {
127133
// timestamps is an array of arrays, each sub-array is in the form ["word", startTime, endTime]'
128134
ts[0] = this.clean(ts[0]);
129-
if (i === 0) {
135+
if (j === 0) {
130136
ts[0] = this.capitalize(ts[0]);
131137
}
132-
if (i === arr.length - 1 && result.final) {
138+
139+
if (j === arr.length - 1 && textFinal) {
133140
ts[0] = this.period(ts[0]);
134141
}
135142
return ts;

test/format-stream-spec.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,23 @@ describe('FormatStream', function() {
8383
stream.write(source);
8484
});
8585

86-
// https://github.com/watson-developer-cloud/speech-javascript-sdk/issues/13
87-
it('should handle %HESITATIONs at the end of a sentence (and not add a period)', function(done) {
86+
it('should handle %HESITATIONs at the beginning, middle, and end of a sentence', function(done) {
8887
var stream = new FormatStream();
8988
stream.setEncoding('utf8');
89+
var source = '%HESITATION asdf %HESITATION bsdf %HESITATION ';
90+
var expected = 'Asdf bsdf. ';
91+
stream.on('data', function(actual) {
92+
assert.equal(actual, expected);
93+
done();
94+
});
95+
stream.on('error', done);
96+
stream.write(source);
97+
});
98+
99+
// https://github.com/watson-developer-cloud/speech-javascript-sdk/issues/13
100+
it('should handle %HESITATIONs at the end of a sentence (and not add a period) when set to ellipsis', function(done) {
101+
var stream = new FormatStream({hesitation: '\u2026'});
102+
stream.setEncoding('utf8');
90103
var source = '%HESITATION asdf %HESITATION ';
91104
var expected = '… asdf … ';
92105
stream.on('data', function(actual) {

0 commit comments

Comments
 (0)