Merge some squashed changes from master

Related: #7, #8, #10, #11, #13, #14, #18, #22, #23, #24, #25, #26, #27
This commit is contained in:
Mark Hillebrand 2019-01-16 16:34:50 +01:00
Родитель 94d2a31cd8
Коммит 57cbb6df12
41 изменённых файлов: 3404 добавлений и 3345 удалений

23
.editorconfig Normal file
Просмотреть файл

@ -0,0 +1,23 @@
root = true
[*.{js,ts}]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
charset = utf-8
insert_final_newline = true
[*.{cmd,json,md,txt,yml}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
charset = utf-8
insert_final_newline = true
[*.sh]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
charset = utf-8
insert_final_newline = true
end_of_line = lf

1
.gitattributes поставляемый
Просмотреть файл

@ -1,3 +1,4 @@
.editorconfig text
.gitattributes text
.gitignore text
.npmignore text

32
.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,32 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Jest All",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand", "--coverage", "false"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
}
},
{
"type": "node",
"request": "launch",
"name": "Jest Current File",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand", "--coverage", "false", "${relativeFile}"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
}
}
]
}

Просмотреть файл

@ -10,32 +10,32 @@ if "%~1" equ "-h" goto :Usage
set TEST_SETTING_FILE_DIR=%~dp0\secrets
if NOT EXIST "%TEST_SETTING_FILE_DIR%" (
md "%TEST_SETTING_FILE_DIR%" || (
echo Error creating directory %TEST_SETTING_FILE_DIR%
exit /b 1
)
md "%TEST_SETTING_FILE_DIR%" || (
echo Error creating directory %TEST_SETTING_FILE_DIR%
exit /b 1
)
)
set TEST_SETTING_FILE_NAME=%TEST_SETTING_FILE_DIR%\TestConfiguration.ts
if EXIST "%TEST_SETTING_FILE_NAME%" (
echo Clearing values from settings file.
echo. > "%TEST_SETTING_FILE_NAME%" || (
echo Error creating file %TEST_SETTING_FILE_NAME%
exit /b 1
)
echo Clearing values from settings file.
echo. > "%TEST_SETTING_FILE_NAME%" || (
echo Error creating file %TEST_SETTING_FILE_NAME%
exit /b 1
)
)
@echo import { Settings } from "../tests/Settings" > "%TEST_SETTING_FILE_NAME%"
:NextArg
if "%~1" == "" (
goto :eof
goto :eof
)
for /f "tokens=1,2 delims=:" %%I in ("%~1") do (
echo Setting Settings.%%I = "%%J"
echo Settings.%%I = "%%J"; >> "%TEST_SETTING_FILE_NAME%"
echo Setting Settings.%%I = "%%J"
echo Settings.%%I = "%%J"; >> "%TEST_SETTING_FILE_NAME%"
)
shift
@ -46,7 +46,7 @@ exit /b 0
:Usage
@echo off
echo.
echo Usage: %~n0 ^<ParamName^>:^<Value^>
echo Usage: %~n0 ^<ParamName^>:^<Value^>
echo.
echo Writes any ^<ParamName^>:^<Value^> pair to the test settings file for JavaScript bindings tests.
echo.

Просмотреть файл

@ -1,55 +1,61 @@
[![npm version](https://badge.fury.io/js/microsoft-cognitiveservices-speech-sdk.svg)](https://badge.fury.io/js/microsoft-cognitiveservices-speech-sdk)
[![Downloads](https://img.shields.io/npm/dm/microsoft-cognitiveservices-speech-sdk.svg)](https://www.npmjs.com/package/microsoft-cognitiveservices-speech-sdk)
# Microsoft Cognitive Services Speech SDK for JavaScript
Visit https://aka.ms/csspeech.
The Microsoft Cognitive Services Speech SDK for JavaScript is the JavaScript version of the Microsoft Cognitive Services Speech SDK. An in-depth description of feature set, functionality, supported platforms, as well as installation options is available [here](https://aka.ms/csspeech).
## Build the project
The JavaScript versions of the Cognitive Services Speech SDK supports browser scenarios as well as the Node.js environment.
To build the project you need to install the required packages, and then run the actual build.
## Installing
### Install the required packages
For the latest stable version:
Installation of the required packages is required once in your enlistment. Our automated build restores the packages with the following command:
```
npm ci
```bash
npm install microsoft-cognitiveservices-speech-sdk
```
This will install packages exactly matching the package configuration from `package-lock.json`.
## Documentation
In a development environment you can also use the following command:
* [Quick tutorial - Node.js](https://docs.microsoft.com/azure/cognitive-services/speech-service/quickstart-js-node)
* [Quick tutorial - Browser](https://docs.microsoft.com/azure/cognitive-services/speech-service/quickstart-js-browser)
* [API Reference](https://aka.ms/csspeech/javascriptref)
* [Samples](https://aka.ms/csspeech/samples)
* [Speech SDK Homepage](https://aka.ms/csspeech)
## Building
This source code for the Cognitive Services Speeck SDK (JavaScript) is available in a public [GitHub repository](https://github.com/Microsoft/cognitive-services-speech-sdk-js). You are not required to go through the build process. We create prebuilt packages tuned for your use-cases. These are updated in regular intervals.
In order to build the Speech SDK, ensure that you have [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/) installed.
Clone the repository:
```bash
git clone https://github.com/Microsoft/cognitive-services-speech-sdk-js
```
Change to the Speech SDK directory:
```bash
cd cognitive-services-speech-sdk-js
```
Install the required packages:
```
npm install
```
### Run the build
Once the dependencies are installed run the build by
Run the build:
```
npm run build
```
or
```
npx gulp bundle
```
or
```
npx gulp compress
```
> Note: `npx` is packaged with NPM 5.2 or above. Update NPM / Node if you
> don't have it or install it globally with `npm install -g npx` (less
> preferable).
## Data / Telemetry
This project collects data and sends it to Microsoft to help monitor our
service performance and improve our products and services. Read the [Microsoft
This project collects data and sends it to Microsoft to help monitor our service performance and improve our products and services. Read the [Microsoft
Privacy Statement](https://aka.ms/csspeech/privacy) to learn more.
To disable telemetry, you can call the following API:
@ -59,26 +65,16 @@ To disable telemetry, you can call the following API:
sdk.Recognizer.enableTelemetry(false);
```
This is a global setting and will disable telemetry for all recognizers
(already created or new recognizers).
This is a global setting and will disable telemetry for all recognizers (already created or new recognizers).
We strongly recommend you keep telemetry enabled. With telemetry enabled you
transmit information about your platform (operating system and possibly, Speech
Service relevant information like microphone characteristics, etc.), and
information about the performance of the Speech Service (the time when you did
send data and when you received data). It can be used to tune the service,
monitor service performance and stability, and might help us to analyze
reported problems. Without telemetry enabled, it is not possible for us to do any
form of detailed analysis in case of a support request.
We strongly recommend you keep telemetry enabled. With telemetry enabled you transmit information about your platform (operating system and possibly, Speech Service relevant information like microphone characteristics, etc.), and information about the performance of the Speech Service (the time when you did send data and when you received data). It can be used to tune the service, monitor service performance and stability, and might help us to analyze reported problems. Without telemetry enabled, it is not possible for us to do any form of detailed analysis in case of a support request.
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).

Просмотреть файл

@ -14,10 +14,10 @@ set TEST_SETTING_FILE_EXISTED=0
if EXIST "%TEST_SETTING_FILE_NAME%" set TEST_SETTING_FILE_EXISTED=1
if "%~1" NEQ "" (
call "%~dp0BuildTestConfig.cmd" %* || (
echo Error creating test config.
exit /b 1
)
call "%~dp0BuildTestConfig.cmd" %* || (
echo Error creating test config.
exit /b 1
)
) else if %TEST_SETTING_FILE_EXISTED% EQU 0 (
echo Warning: No test config and no parameters specified. This will probably fail. 1>&2
)
@ -28,7 +28,7 @@ call npm run test
set NPM_ERROR=%ERRORLEVEL%
if %TEST_SETTING_FILE_EXISTED% EQU 0 (
del "%TEST_SETTING_FILE_NAME%"
del "%TEST_SETTING_FILE_NAME%"
)
popd

Просмотреть файл

@ -13,20 +13,77 @@ resources:
- repo: self
clean: true
variables:
SPEECHSDK_JS_ROOT: .
jobs:
- job: Pre
- job: Build
pool:
name: Hosted Ubuntu 1604
timeoutInMinutes: 30
name: Hosted VS2017
demands: npm
timeoutInMinutes: 60
variables:
ArtifactOut: $(Build.ArtifactStagingDirectory)/Out/JavaScript
SPEECHSDK_RUN_TESTS: true
steps:
- bash: ./ci/check-git-head.sh
displayName: Repository checks
- template: jsbuild.yml
parameters:
dependsOn: Pre
condition: true
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: Component Detection
condition: >
and(eq(variables['System.CollectionId'], '19422243-19b9-4d85-9ca6-bc961861d287'),
eq(variables['System.DefinitionId'], '7863'),
eq(variables['Build.SourceBranch'], 'refs/heads/master'),
or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.Reason'], 'Manual')))
- bash: npm ci && npm run civersion
displayName: Install packages and set version / SPEECHSDK_SEMVER2NOMETA
- bash: |
F=src/common.speech/RecognizerConfig.ts
[[ -f $F ]] || exit 1
perl -i.bak -p -e 'BEGIN { $c = 0 } $c += s/(?<=const SPEECHSDK_CLIENTSDK_VERSION = ")[^"]*/$(SPEECHSDK_SEMVER2NOMETA)/g; END { die "Patched SPEECHSDK_CLIENTSDK_VERSION $c time(s), expected 1.\n" if $c != 1 }' "$F"
E=$?
rm -f "$F.bak"
git diff
exit $E
displayName: Stamp SPEECHSDK_CLIENTSDK_VERSION
- bash: npm pack
displayName: Build and pack SDK
- bash: "echo '##vso[task.setvariable variable=SPEECHSDK_RUN_TESTS]false'"
condition: or(failed(), canceled())
displayName: Skip tests on build failure
- script: |
RunTests.cmd ^
SpeechSubscriptionKey:$(speech-ne-s0-key1) ^
SpeechRegion:northeurope ^
LuisSubscriptionKey:$(luis-westus-s0-201809-key1) ^
LuisRegion:westus ^
SpeechTestEndpointId:ec3432b2-8584-4736-865a-556213b9f6fd
displayName: Run tests
condition: eq(variables['SPEECHSDK_RUN_TESTS'], 'true')
- task: PublishTestResults@2
displayName: Publish test results
condition: eq(variables['SPEECHSDK_RUN_TESTS'], 'true')
- bash: |
set -u -e -o pipefail -x
PACKAGE_BASE=microsoft-cognitiveservices-speech-sdk
PACKAGE_NAME=$PACKAGE_BASE-$SPEECHSDK_SEMVER2NOMETA.tgz
PACKAGE_IN=$PACKAGE_NAME
PACKAGE_OUT="/npm"
ZIP_OUT="$(ArtifactOut)/SpeechSDK-JavaScript-$SPEECHSDK_SEMVER2NOMETA"
mkdir -p "$PACKAGE_OUT" "$ZIP_OUT"
cp --preserve "$PACKAGE_IN" "$PACKAGE_OUT"
echo SRI hash for microsoft.cognitiveservices.speech.sdk.bundle.js: sha512-"$(openssl dgst -sha512 -binary distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.js | openssl base64 -A)"
cp --preserve LICENSE REDIST.txt distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.* distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle-min.* "$ZIP_OUT"
displayName: Create drop
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: $(ArtifactOut)/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA)
includeRootFolder: true
archiveType: zip
archiveFile: $(ArtifactOut)/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA).zip
displayName: Create .zip
- bash: rm -rf "$(ArtifactOut)/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA)"
displayName: Remove temporary directory
- task: PublishBuildArtifacts@1
displayName: Publish drop
inputs:
PathtoPublish: $(ArtifactOut)
ArtifactName: JavaScript
publishLocation: Container

Просмотреть файл

@ -1,72 +0,0 @@
#
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.
#
parameters:
dependsOn: ''
condition: succeeded()
jobs:
- job: JsBuild
dependsOn: ${{ parameters.dependsOn }}
condition: ${{ parameters.condition }}
pool:
name: Hosted VS2017
demands: npm
timeoutInMinutes: 60
steps:
- bash: npm ci && npm run civersion
displayName: Set version
workingDirectory: $(SPEECHSDK_JS_ROOT)
- bash: |
F=$(SPEECHSDK_JS_ROOT)/src/common.speech/RecognizerConfig.ts
[[ -f $F ]] || exit 1
perl -i.bak -p -e 'BEGIN { $c = 0 } $c += s/(?<=const SPEECHSDK_CLIENTSDK_VERSION = ")[^"]*/$(SPEECHSDK_SEMVER2NOMETA)/g; END { die "Patched SPEECHSDK_CLIENTSDK_VERSION $c time(s), expected 1.\n" if $c != 1 }' "$F"
E=$?
rm -f "$F.bak"
git diff
exit $E
displayName: Stamp SPEECHSDK_CLIENTSDK_VERSION
- bash: npm run build && npm pack
displayName: Build and pack SDK
workingDirectory: $(SPEECHSDK_JS_ROOT)
- script: |
RunTests.cmd ^
SpeechSubscriptionKey:$(speech-ne-s0-key1) ^
SpeechRegion:northeurope ^
LuisSubscriptionKey:$(luis-westus-s0-201809-key1) ^
LuisRegion:westus ^
SpeechTestEndpointId:ec3432b2-8584-4736-865a-556213b9f6fd
displayName: Run tests
workingDirectory: $(SPEECHSDK_JS_ROOT)
- task: PublishTestResults@2
displayName: Publish test results
- bash: |
set -u -e -o pipefail -x
PACKAGE_BASE=microsoft-cognitiveservices-speech-sdk
PACKAGE_NAME=$PACKAGE_BASE-$SPEECHSDK_SEMVER2NOMETA.tgz
PACKAGE_IN=$(SPEECHSDK_JS_ROOT)/$PACKAGE_NAME
PACKAGE_OUT="$(Build.ArtifactStagingDirectory)/Out/JavaScript/npm"
ZIP_OUT="$(Build.ArtifactStagingDirectory)/Out/JavaScript/SpeechSDK-JavaScript-$SPEECHSDK_SEMVER2NOMETA"
mkdir -p "$PACKAGE_OUT" "$ZIP_OUT"
cp --preserve "$PACKAGE_IN" "$PACKAGE_OUT"
echo SRI hash for microsoft.cognitiveservices.speech.sdk.bundle.js: sha512-"$(openssl dgst -sha512 -binary $(SPEECHSDK_JS_ROOT)/distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.js | openssl base64 -A)"
cp --preserve $(SPEECHSDK_JS_ROOT)/LICENSE $(SPEECHSDK_JS_ROOT)/REDIST.txt $(SPEECHSDK_JS_ROOT)/distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.* $(SPEECHSDK_JS_ROOT)/distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle-min.* "$ZIP_OUT"
displayName: Create drop
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: $(Build.ArtifactStagingDirectory)/Out/JavaScript/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA)
includeRootFolder: true
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/Out/JavaScript/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA).zip
displayName: Create .zip
- bash: rm -rf "$(Build.ArtifactStagingDirectory)/Out/JavaScript/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA)"
displayName: Remove temporary directory
- task: PublishBuildArtifacts@1
displayName: Publish drop
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)/Out/JavaScript'
ArtifactName: JavaScript
publishLocation: Container

Просмотреть файл

@ -4,80 +4,80 @@
//
(function () {
'use strict';
var semver = require("semver");
var exec = require("child_process").exec;
'use strict';
var semver = require("semver");
var exec = require("child_process").exec;
if (!process.env.npm_package_version) {
throw "npm_package_version not set; run this via 'npm run'"
}
var givenVersion = process.env.npm_package_version;
if (!semver.valid(givenVersion)) {
throw "Invalid version " + givenVersion;
}
var baseVersion = semver.major(givenVersion) + "." + semver.minor(givenVersion) + "." + semver.patch(givenVersion);
var prerelease = semver.prerelease(givenVersion) || []
var buildId = process.env.BUILD_BUILDID || "1"
var buildType = "dev"
var inAzureDevOps = false
if (process.env.SYSTEM_COLLECTIONID === "19422243-19b9-4d85-9ca6-bc961861d287" &&
(process.env.SYSTEM_DEFINITIONID === "4833" || process.env.SYSTEM_DEFINITIONID === "7863")) {
inAzureDevOps = true
if (process.env.BUILD_SOURCEBRANCH.match("^refs/heads/release/")) {
buildType = "prod"
} else if (process.env.BUILD_SOURCEBRANCH === "refs/heads/master" &&
(process.env.BUILD_REASON === "Schedule" ||
process.env.BUILD_REASON === "Manual")) {
buildType = "int"
if (!process.env.npm_package_version) {
throw "npm_package_version not set; run this via 'npm run'"
}
}
// Check our version constraints
if (buildType === "prod") {
// Full version give in package.json
if (prerelease.length != 0 &&
(prerelease.length != 2 ||
!prerelease[0].match("^(?:alpha|beta|rc)$") ||
prerelease[1] < 1)) {
throw "For prod build types, version must have no pre-release tag, or alpha / beta / rc with a positive number."
var givenVersion = process.env.npm_package_version;
if (!semver.valid(givenVersion)) {
throw "Invalid version " + givenVersion;
}
} else if (prerelease.length != 3 ||
(prerelease[0] !== "alpha" || prerelease[1] !== 0 || prerelease[2] !== 1)) {
throw "For non-prod build types, checked-in version must end in -alpha.0.1"
}
var versionToUse
var baseVersion = semver.major(givenVersion) + "." + semver.minor(givenVersion) + "." + semver.patch(givenVersion);
var prerelease = semver.prerelease(givenVersion) || []
if (buildType === "dev") {
versionToUse = baseVersion + "-alpha.0." + buildId
} else if (buildType === "int") {
versionToUse = baseVersion + "-beta.0." + buildId
} else if (buildType === "prod") {
versionToUse = givenVersion
} else {
throw "Internal error. Unexpected build type: " + buildType
}
var buildId = process.env.BUILD_BUILDID || "1"
if (inAzureDevOps) {
console.log("##vso[task.setvariable variable=SPEECHSDK_SEMVER2NOMETA]" + versionToUse);
}
var buildType = "dev"
var inAzureDevOps = false
if (givenVersion !== versionToUse) {
console.log("Setting version to " + versionToUse);
exec("npm version --no-git-tag-version " + versionToUse, { cwd: __dirname + "/.." },
function (error) {
if (error) {
throw error;
if (process.env.SYSTEM_COLLECTIONID === "19422243-19b9-4d85-9ca6-bc961861d287" &&
process.env.SYSTEM_DEFINITIONID === "7863") {
inAzureDevOps = true
if (process.env.BUILD_SOURCEBRANCH.match("^refs/heads/release/")) {
buildType = "prod"
} else if (process.env.BUILD_SOURCEBRANCH === "refs/heads/master" &&
(process.env.BUILD_REASON === "Schedule" ||
process.env.BUILD_REASON === "Manual")) {
buildType = "int"
}
}
);
}
}
// Check our version constraints
if (buildType === "prod") {
// Full version give in package.json
if (prerelease.length != 0 &&
(prerelease.length != 2 ||
!prerelease[0].match("^(?:alpha|beta|rc)$") ||
prerelease[1] < 1)) {
throw "For prod build types, version must have no pre-release tag, or alpha / beta / rc with a positive number."
}
} else if (prerelease.length != 3 ||
(prerelease[0] !== "alpha" || prerelease[1] !== 0 || prerelease[2] !== 1)) {
throw "For non-prod build types, checked-in version must end in -alpha.0.1"
}
var versionToUse
if (buildType === "dev") {
versionToUse = baseVersion + "-alpha.0." + buildId
} else if (buildType === "int") {
versionToUse = baseVersion + "-beta.0." + buildId
} else if (buildType === "prod") {
versionToUse = givenVersion
} else {
throw "Internal error. Unexpected build type: " + buildType
}
if (inAzureDevOps) {
console.log("##vso[task.setvariable variable=SPEECHSDK_SEMVER2NOMETA]" + versionToUse);
}
if (givenVersion !== versionToUse) {
console.log("Setting version to " + versionToUse);
exec("npm version --no-git-tag-version " + versionToUse, { cwd: __dirname + "/.." },
function (error) {
if (error) {
throw error;
}
}
);
}
}());

Просмотреть файл

@ -1,35 +1,35 @@
{
"templates": {
"applicationName": "Microsoft Cognitive Services Speech SDK",
"disqus": "",
"googleAnalytics": "",
"openGraph": {
"title": "",
"type": "website",
"image": "",
"site_name": "",
"url": ""
}
},
"meta": {
"title": "Microsoft Cognitive Services Speech SDK",
"description": "Microsoft Cognitive Services Speech SDK",
"keyword": ""
},
"linenums": true,
"source": {
"include": [
"./distrib/src/sdk"
],
"includePattern": ".+\\.js(doc)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"opts": {
"encoding": "utf8",
"recurse": true,
"private": false,
"lenient": true,
"destination": "./outjs",
"template": "templates/default"
"templates": {
"applicationName": "Microsoft Cognitive Services Speech SDK",
"disqus": "",
"googleAnalytics": "",
"openGraph": {
"title": "",
"type": "website",
"image": "",
"site_name": "",
"url": ""
}
},
"meta": {
"title": "Microsoft Cognitive Services Speech SDK",
"description": "Microsoft Cognitive Services Speech SDK",
"keyword": ""
},
"linenums": true,
"source": {
"include": [
"./distrib/src/sdk"
],
"includePattern": ".+\\.js(doc)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"opts": {
"encoding": "utf8",
"recurse": true,
"private": false,
"lenient": true,
"destination": "./outjs",
"template": "templates/default"
}
}

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { ConsoleLoggingListener, LocalStorage, SessionStorage } from "./src/common.browser/Exports";
import { Events, Storage } from "./src/common/Exports";
import { ConsoleLoggingListener } from "./src/common.browser/Exports";
import { Events } from "./src/common/Exports";
// Common.Storage.SetLocalStorage(new Common.Browser.LocalStorage());
// Common.Storage.SetSessionStorage(new Common.Browser.SessionStorage());

4795
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -2,7 +2,7 @@
"name": "microsoft-cognitiveservices-speech-sdk",
"author": "Microsoft Corporation",
"homepage": "https://docs.microsoft.com/azure/cognitive-services/speech-service/",
"version": "1.2.0",
"version": "1.3.0-alpha.0.1",
"license": "MIT",
"description": "Microsoft Cognitive Services Speech SDK for JavaScript",
"keywords": [
@ -34,19 +34,21 @@
"REDIST.txt"
],
"devDependencies": {
"@types/jest": "^23.3.1",
"@types/jest": "^23.3.10",
"@types/node": "^10.7.0",
"@types/request": "^2.48.1",
"gulp": "^4.0.0",
"gulp-uglify": "^3.0.1",
"gulp-rename": "^1.4.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-tslint": "^8.0.0",
"gulp-typescript": "^3.2.3",
"jest": "^23.5.0",
"gulp-uglify": "^3.0.1",
"jest": "^23.6.0",
"jest-junit": "^5.1.0",
"request": "^2.88.0",
"semver": "^5.6.0",
"source-map-loader": "^0.2.3",
"ts-jest": "^23.1.3",
"ts-jest": "^23.10.5",
"tslint": "^5.11.0",
"typescript": "^3.2.1",
"webpack-stream": "^4.0.0"

Просмотреть файл

@ -3,12 +3,10 @@
export * from "./ConsoleLoggingListener";
export * from "./IRecorder";
export * from "./LocalStorage";
export * from "./MicAudioSource";
export * from "./FileAudioSource";
export * from "./OpusRecorder";
export * from "./PCMRecorder";
export * from "./SessionStorage";
export * from "./WebsocketConnection";
export * from "./WebsocketMessageAdapter";
export * from "./ReplayableAudioNode";

Просмотреть файл

@ -1,44 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { ArgumentNullError, IKeyValueStorage } from "../common/Exports";
export class LocalStorage implements IKeyValueStorage {
public get = (key: string): string => {
if (!key) {
throw new ArgumentNullError("key");
}
return localStorage.getItem(key);
}
public getOrAdd = (key: string, valueToAdd: string): string => {
if (!key) {
throw new ArgumentNullError("key");
}
const value = localStorage.getItem(key);
if (value === null || value === undefined) {
localStorage.setItem(key, valueToAdd);
}
return localStorage.getItem(key);
}
public set = (key: string, value: string): void => {
if (!key) {
throw new ArgumentNullError("key");
}
localStorage.setItem(key, value);
}
public remove = (key: string): void => {
if (!key) {
throw new ArgumentNullError("key");
}
localStorage.removeItem(key);
}
}

Просмотреть файл

@ -1,44 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { ArgumentNullError, IKeyValueStorage } from "../common/Exports";
export class SessionStorage implements IKeyValueStorage {
public get = (key: string): string => {
if (!key) {
throw new ArgumentNullError("key");
}
return sessionStorage.getItem(key);
}
public getOrAdd = (key: string, valueToAdd: string): string => {
if (!key) {
throw new ArgumentNullError("key");
}
const value = sessionStorage.getItem(key);
if (value === null || value === undefined) {
sessionStorage.setItem(key, valueToAdd);
}
return sessionStorage.getItem(key);
}
public set = (key: string, value: string): void => {
if (!key) {
throw new ArgumentNullError("key");
}
sessionStorage.setItem(key, value);
}
public remove = (key: string): void => {
if (!key) {
throw new ArgumentNullError("key");
}
sessionStorage.removeItem(key);
}
}

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT license.
import { WebsocketConnection } from "../common.browser/Exports";
import { IConnection, IStringDictionary, Storage } from "../common/Exports";
import { IConnection, IStringDictionary } from "../common/Exports";
import { PropertyId } from "../sdk/Exports";
import { AuthInfo, IConnectionFactory, RecognizerConfig, WebsocketMessageFormatter } from "./Exports";
@ -20,7 +20,7 @@ export class IntentConnectionFactory implements IConnectionFactory {
if (!endpoint) {
const region: string = config.parameters.getProperty(PropertyId.SpeechServiceConnection_IntentRegion);
endpoint = this.host() + Storage.local.getOrAdd("TranslationRelativeUri", "/speech/" + this.getSpeechRegionFromIntentRegion(region) + "/recognition/interactive/cognitiveservices/v1");
endpoint = "wss://speech.platform.bing.com/speech/" + this.getSpeechRegionFromIntentRegion(region) + "/recognition/interactive/cognitiveservices/v1";
}
const queryParams: IStringDictionary<string> = {
@ -28,10 +28,6 @@ export class IntentConnectionFactory implements IConnectionFactory {
language: config.parameters.getProperty(PropertyId.SpeechServiceConnection_RecoLanguage),
};
if (this.isDebugModeEnabled) {
queryParams[TestHooksParamName] = "1";
}
const headers: IStringDictionary<string> = {};
headers[authInfo.headerName] = authInfo.token;
headers[ConnectionIdHeader] = connectionId;
@ -39,15 +35,6 @@ export class IntentConnectionFactory implements IConnectionFactory {
return new WebsocketConnection(endpoint, queryParams, headers, new WebsocketMessageFormatter(), connectionId);
}
private host(): string {
return Storage.local.getOrAdd("Host", "wss://speech.platform.bing.com");
}
private get isDebugModeEnabled(): boolean {
const value = Storage.local.getOrAdd("IsDebugModeEnabled", "false");
return value.toLowerCase() === "true";
}
private getSpeechRegionFromIntentRegion(intentRegion: string): string {
switch (intentRegion) {
case "West US":

Просмотреть файл

@ -53,6 +53,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
private privAuthFetchEventId: string;
private privIsDisposed: boolean;
private privRecognizer: Recognizer;
private privMustReportEndOfStream: boolean;
protected privRecognizerConfig: RecognizerConfig;
public constructor(
@ -78,6 +79,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
throw new ArgumentNullError("recognizerConfig");
}
this.privMustReportEndOfStream = false;
this.privAuthentication = authentication;
this.privConnectionFactory = connectionFactory;
this.privAudioSource = audioSource;
@ -189,7 +191,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
} catch { }
}
return this.fetchConnection(requestSession).onSuccessContinueWithPromise((connection: IConnection): Promise<boolean> => {
return this.fetchConnection(requestSession).onSuccessContinueWith((connection: IConnection): Promise<boolean> => {
return connection.send(new SpeechConnectionMessage(
MessageType.Text,
"telemetry",
@ -295,13 +297,13 @@ export abstract class ServiceRecognizerBase implements IDisposable {
requestSession: RequestSession,
successCallback: (e: SpeechRecognitionResult) => void,
errorCallBack: (e: string) => void,
): Promise<boolean> => {
return this.fetchConnection(requestSession).onSuccessContinueWithPromise((connection: IConnection): Promise<boolean> => {
): Promise<IConnection> => {
return this.fetchConnection(requestSession).on((connection: IConnection): Promise<IConnection> => {
return connection.read()
.onSuccessContinueWithPromise((message: ConnectionMessage) => {
if (this.privIsDisposed) {
// We're done.
return PromiseHelper.fromResult(true);
return PromiseHelper.fromResult(undefined);
}
// indicates we are draining the queue and it came with no message;
@ -318,6 +320,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
if (connectionMessage.requestId.toLowerCase() === requestSession.requestId.toLowerCase()) {
switch (connectionMessage.path.toLowerCase()) {
case "turn.start":
this.privMustReportEndOfStream = true;
break;
case "speech.startdetected":
const speechStartDetected: SpeechDetected = SpeechDetected.fromJSON(connectionMessage.textBody);
@ -349,12 +352,13 @@ export abstract class ServiceRecognizerBase implements IDisposable {
if (!!this.privRecognizer.speechEndDetected) {
this.privRecognizer.speechEndDetected(this.privRecognizer, speechStopEventArgs);
}
if (requestSession.isSpeechEnded && this.privRecognizerConfig.isContinuousRecognition) {
this.cancelRecognitionLocal(requestSession, CancellationReason.EndOfStream, CancellationErrorCode.NoError, undefined, successCallback);
}
break;
case "turn.end":
if (requestSession.isSpeechEnded && this.privMustReportEndOfStream) {
this.privMustReportEndOfStream = false;
this.cancelRecognitionLocal(requestSession, CancellationReason.EndOfStream, CancellationErrorCode.NoError, undefined, successCallback);
}
const sessionStopEventArgs: SessionEventArgs = new SessionEventArgs(requestSession.sessionId);
requestSession.onServiceTurnEndResponse(this.privRecognizerConfig.isContinuousRecognition);
if (!this.privRecognizerConfig.isContinuousRecognition || requestSession.isSpeechEnded) {
@ -380,6 +384,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
return this.receiveMessage(requestSession, successCallback, errorCallBack);
});
}, (error: string) => {
});
}
@ -437,11 +442,11 @@ export abstract class ServiceRecognizerBase implements IDisposable {
const audioFormat: AudioStreamFormatImpl = this.privAudioSource.format as AudioStreamFormatImpl;
const readAndUploadCycle = (_: boolean) => {
const readAndUploadCycle = () => {
// If speech is done, stop sending audio.
if (!this.privIsDisposed && !requestSession.isSpeechEnded && !requestSession.isCompleted) {
this.fetchConnection(requestSession).onSuccessContinueWith((connection: IConnection) => {
this.fetchConnection(requestSession).on((connection: IConnection) => {
audioStreamNode.read().on(
(audioStreamChunk: IStreamChunk<ArrayBuffer>) => {
@ -464,10 +469,13 @@ export abstract class ServiceRecognizerBase implements IDisposable {
const delay: number = Math.max(0, (lastSendTime - Date.now() + minSendTime));
uploaded.onSuccessContinueWith((result: boolean) => {
uploaded.continueWith((_: PromiseResult<boolean>) => {
// Regardless of success or failure, schedule the next upload.
// If the underlying connection was broken, the next cycle will
// get a new connection and re-transmit missing audio automatically.
setTimeout(() => {
lastSendTime = Date.now();
readAndUploadCycle(result);
readAndUploadCycle();
}, delay);
});
} else {
@ -489,11 +497,13 @@ export abstract class ServiceRecognizerBase implements IDisposable {
deferred.reject(error);
}
});
}, (error: string) => {
deferred.reject(error);
});
}
};
readAndUploadCycle(true);
readAndUploadCycle();
return deferred.promise();
}

Просмотреть файл

@ -3,13 +3,17 @@
import { WebsocketConnection } from "../common.browser/Exports";
import { OutputFormatPropertyName } from "../common.speech/Exports";
import { IConnection, IStringDictionary, Storage } from "../common/Exports";
import { IConnection, IStringDictionary } from "../common/Exports";
import { OutputFormat, PropertyId } from "../sdk/Exports";
import { AuthInfo, IConnectionFactory, RecognitionMode, RecognizerConfig, WebsocketMessageFormatter } from "./Exports";
import { QueryParameterNames } from "./QueryParameterNames";
export class SpeechConnectionFactory implements IConnectionFactory {
private readonly interactiveRelativeUri: string = "/speech/recognition/interactive/cognitiveservices/v1";
private readonly conversationRelativeUri: string = "/speech/recognition/conversation/cognitiveservices/v1";
private readonly dictationRelativeUri: string = "/speech/recognition/dictation/cognitiveservices/v1";
public create = (
config: RecognizerConfig,
authInfo: AuthInfo,
@ -36,22 +40,20 @@ export class SpeechConnectionFactory implements IConnectionFactory {
queryParams[QueryParameterNames.FormatParamName] = config.parameters.getProperty(OutputFormatPropertyName, OutputFormat[OutputFormat.Simple]).toLowerCase();
}
if (this.isDebugModeEnabled) {
queryParams[QueryParameterNames.TestHooksParamName] = "1";
}
if (!endpoint) {
const region: string = config.parameters.getProperty(PropertyId.SpeechServiceConnection_Region, undefined);
const host: string = "wss://" + region + ".stt.speech.microsoft.com";
switch (config.recognitionMode) {
case RecognitionMode.Conversation:
endpoint = this.host(region) + this.conversationRelativeUri;
endpoint = host + this.conversationRelativeUri;
break;
case RecognitionMode.Dictation:
endpoint = this.host(region) + this.dictationRelativeUri;
endpoint = host + this.dictationRelativeUri;
break;
default:
endpoint = this.host(region) + this.interactiveRelativeUri; // default is interactive
endpoint = host + this.interactiveRelativeUri; // default is interactive
break;
}
}
@ -62,25 +64,4 @@ export class SpeechConnectionFactory implements IConnectionFactory {
return new WebsocketConnection(endpoint, queryParams, headers, new WebsocketMessageFormatter(), connectionId);
}
private host(region: string): string {
return Storage.local.getOrAdd("Host", "wss://" + region + ".stt.speech.microsoft.com");
}
private get interactiveRelativeUri(): string {
return Storage.local.getOrAdd("InteractiveRelativeUri", "/speech/recognition/interactive/cognitiveservices/v1");
}
private get conversationRelativeUri(): string {
return Storage.local.getOrAdd("ConversationRelativeUri", "/speech/recognition/conversation/cognitiveservices/v1");
}
private get dictationRelativeUri(): string {
return Storage.local.getOrAdd("DictationRelativeUri", "/speech/recognition/dictation/cognitiveservices/v1");
}
private get isDebugModeEnabled(): boolean {
const value = Storage.local.getOrAdd("IsDebugModeEnabled", "false");
return value.toLowerCase() === "true";
}
}

Просмотреть файл

@ -87,7 +87,7 @@ export class SpeechServiceRecognizer extends ServiceRecognizerBase {
const simple: SimpleSpeechPhrase = SimpleSpeechPhrase.fromJSON(connectionMessage.textBody);
const resultReason: ResultReason = EnumTranslation.implTranslateRecognitionResult(simple.RecognitionStatus);
requestSession.onServiceRecognized(requestSession.currentTurnAudioOffset + simple.Offset);
requestSession.onServiceRecognized(requestSession.currentTurnAudioOffset + simple.Offset + simple.Duration);
if (ResultReason.Canceled === resultReason) {
const cancelReason: CancellationReason = EnumTranslation.implTranslateCancelResult(simple.RecognitionStatus);

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT license.
import { WebsocketConnection } from "../common.browser/Exports";
import { IConnection, IStringDictionary, Storage } from "../common/Exports";
import { IConnection, IStringDictionary } from "../common/Exports";
import { PropertyId } from "../sdk/Exports";
import { AuthInfo, IConnectionFactory, RecognizerConfig, WebsocketMessageFormatter } from "./Exports";
@ -20,7 +20,7 @@ export class TranslationConnectionFactory implements IConnectionFactory {
if (!endpoint) {
const region: string = config.parameters.getProperty(PropertyId.SpeechServiceConnection_Region, undefined);
endpoint = this.host(region) + Storage.local.getOrAdd("TranslationRelativeUri", "/speech/translation/cognitiveservices/v1");
endpoint = "wss://" + region + ".s2s.speech.microsoft.com/speech/translation/cognitiveservices/v1";
}
const queryParams: IStringDictionary<string> = {
@ -28,10 +28,6 @@ export class TranslationConnectionFactory implements IConnectionFactory {
to: config.parameters.getProperty(PropertyId.SpeechServiceConnection_TranslationToLanguages),
};
if (this.isDebugModeEnabled) {
queryParams[TestHooksParamName] = "1";
}
const voiceName: string = "voice";
const featureName: string = "features";
@ -46,13 +42,4 @@ export class TranslationConnectionFactory implements IConnectionFactory {
return new WebsocketConnection(endpoint, queryParams, headers, new WebsocketMessageFormatter(), connectionId);
}
private host(region: string): string {
return Storage.local.getOrAdd("Host", "wss://" + region + ".s2s.speech.microsoft.com");
}
private get isDebugModeEnabled(): boolean {
const value = Storage.local.getOrAdd("IsDebugModeEnabled", "false");
return value.toLowerCase() === "true";
}
}

Просмотреть файл

@ -80,6 +80,8 @@ export class TranslationServiceRecognizer extends ServiceRecognizerBase {
const translatedPhrase: TranslationPhrase = TranslationPhrase.fromJSON(connectionMessage.textBody);
if (translatedPhrase.RecognitionStatus === RecognitionStatus.Success) {
requestSession.onServiceRecognized(requestSession.currentTurnAudioOffset + translatedPhrase.Offset + translatedPhrase.Duration);
// OK, the recognition was successful. How'd the translation do?
const result: TranslationRecognitionEventArgs = this.fireEventForResult(translatedPhrase, requestSession);
if (!!this.privTranslationRecognizer.recognized) {
@ -292,18 +294,20 @@ export class TranslationServiceRecognizer extends ServiceRecognizerBase {
resultReason = ResultReason.TranslatingSpeech;
}
const offset: number = serviceResult.Offset + requestSession.currentTurnAudioOffset;
const result = new TranslationRecognitionResult(
translations,
requestSession.requestId,
resultReason,
serviceResult.Text,
serviceResult.Duration,
serviceResult.Offset,
offset,
serviceResult.Translation.FailureReason,
JSON.stringify(serviceResult),
undefined);
const ev = new TranslationRecognitionEventArgs(result, serviceResult.Offset, requestSession.sessionId);
const ev = new TranslationRecognitionEventArgs(result, offset, requestSession.sessionId);
return ev;
}

Просмотреть файл

@ -15,8 +15,6 @@ export * from "./IDetachable";
export * from "./IDictionary";
export * from "./IDisposable";
export * from "./IEventSource";
export * from "./IKeyValueStorage";
export * from "./InMemoryStorage";
export * from "./ITimer";
export * from "./IWebsocketMessageFormatter";
export * from "./List";
@ -25,6 +23,5 @@ export * from "./Promise";
export * from "./Queue";
export * from "./RawWebsocketMessage";
export * from "./RiffPcmEncoder";
export * from "./Storage";
export * from "./Stream";
export { TranslationStatus } from "../common.speech/TranslationStatus";

Просмотреть файл

@ -1,9 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export interface IKeyValueStorage {
get(key: string): string;
getOrAdd(key: string, valueToAdd: string): string;
set(key: string, value: string): void;
remove(key: string): void;
}

Просмотреть файл

@ -1,49 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { ArgumentNullError } from "./Error";
import { IStringDictionary } from "./IDictionary";
import { IKeyValueStorage } from "./IKeyValueStorage";
export class InMemoryStorage implements IKeyValueStorage {
private privStore: IStringDictionary<string> = {};
public get = (key: string): string => {
if (!key) {
throw new ArgumentNullError("key");
}
return this.privStore[key];
}
public getOrAdd = (key: string, valueToAdd: string): string => {
if (!key) {
throw new ArgumentNullError("key");
}
if (this.privStore[key] === undefined) {
this.privStore[key] = valueToAdd;
}
return this.privStore[key];
}
public set = (key: string, value: string): void => {
if (!key) {
throw new ArgumentNullError("key");
}
this.privStore[key] = value;
}
public remove = (key: string): void => {
if (!key) {
throw new ArgumentNullError("key");
}
if (this.privStore[key] !== undefined) {
delete this.privStore[key];
}
}
}

Просмотреть файл

@ -1,35 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import { ArgumentNullError } from "./Error";
import { IKeyValueStorage } from "./IKeyValueStorage";
import { InMemoryStorage } from "./InMemoryStorage";
export class Storage {
private static privSessionStorage: IKeyValueStorage = new InMemoryStorage();
private static privLocalStorage: IKeyValueStorage = new InMemoryStorage();
public static setSessionStorage = (sessionStorage: IKeyValueStorage): void => {
if (!sessionStorage) {
throw new ArgumentNullError("sessionStorage");
}
Storage.privSessionStorage = sessionStorage;
}
public static setLocalStorage = (localStorage: IKeyValueStorage): void => {
if (!localStorage) {
throw new ArgumentNullError("localStorage");
}
Storage.privLocalStorage = localStorage;
}
public static get session(): IKeyValueStorage {
return Storage.privSessionStorage;
}
public static get local(): IKeyValueStorage {
return Storage.privLocalStorage;
}
}

Просмотреть файл

@ -114,7 +114,8 @@ export class IntentRecognizer extends Recognizer {
}
/**
* Sets the authorization token used to communicate with the service.
* Gets/Sets the authorization token used to communicate with the service.
* Note: Please use a token derived from your LanguageUnderstanding subscription key for the Intent recognizer.
* @member IntentRecognizer.prototype.authorizationToken
* @function
* @public

Просмотреть файл

@ -49,10 +49,10 @@ export enum PropertyId {
SpeechServiceAuthorization_Type,
/**
* The Cognitive Services Custom Speech Service endpoint id. Under normal circumstances, you shouldn't
* The Cognitive Services Speech Service endpoint id. Under normal circumstances, you shouldn't
* have to use this property directly.
* Instead, use @see SpeechConfig.setEndpointId.
* NOTE: The endpoint id is available in the Custom Speech Portal, listed under Endpoint Details.
* NOTE: The endpoint id is available in the Speech Portal, listed under Endpoint Details.
* @member PropertyId.SpeechServiceConnection_EndpointId
*/
SpeechServiceConnection_EndpointId,

Просмотреть файл

@ -18,6 +18,7 @@ export abstract class SpeechConfig {
/**
* Static instance of SpeechConfig returned by passing subscriptionKey and service region.
* Note: Please use your LanguageUnderstanding subscription key in case you want to use the Intent recognizer.
* @member SpeechConfig.fromSubscription
* @function
* @public
@ -41,6 +42,7 @@ export abstract class SpeechConfig {
* Creates an instance of the speech factory with specified endpoint and subscription key.
* This method is intended only for users who use a non-standard service endpoint or paramters.
* the language setting in uri takes precedence, and the effective language is "de-DE".
* Note: Please use your LanguageUnderstanding subscription key in case you want to use the Intent recognizer.
* @member SpeechConfig.fromEndpoint
* @function
* @public
@ -60,6 +62,7 @@ export abstract class SpeechConfig {
/**
* Creates an instance of the speech factory with specified initial authorization token and region.
* Note: Please use a token derived from your LanguageUnderstanding subscription key in case you want to use the Intent recognizer.
* @member SpeechConfig.fromAuthorizationToken
* @function
* @public
@ -87,8 +90,8 @@ export abstract class SpeechConfig {
public abstract get authorizationToken(): string;
/**
* Sets the authorization token.
* If this is set, subscription key is ignored.
* Get/Sets the authorization token.
* If the autorizaton token is set, the subscription key is ignored.
* User needs to make sure the provided authorization token is valid and not expired.
* @member SpeechConfig.prototype.authorizationToken
* @function
@ -106,7 +109,7 @@ export abstract class SpeechConfig {
public abstract get speechRecognitionLanguage(): string;
/**
* Sets the input language.
* Gets/Sets the input language.
* @member SpeechConfig.prototype.speechRecognitionLanguage
* @function
* @public
@ -135,14 +138,6 @@ export abstract class SpeechConfig {
*/
public abstract getProperty(name: string, def?: string): string;
/**
* Sets output format.
* @member SpeechConfig.prototype.outputFormat
* @function
* @public
*/
public abstract set outputFormat(format: OutputFormat);
/**
* Gets output format.
* @member SpeechConfig.prototype.outputFormat
@ -153,13 +148,12 @@ export abstract class SpeechConfig {
public abstract get outputFormat(): OutputFormat;
/**
* Sets the endpoint ID of a customized speech model that is used for speech recognition.
* @member SpeechConfig.prototype.endpointId
* Gets/Sets the output format.
* @member SpeechConfig.prototype.outputFormat
* @function
* @public
* @param {string} value - The endpoint ID
*/
public abstract set endpointId(value: string);
public abstract set outputFormat(format: OutputFormat);
/**
* Gets the endpoint ID of a customized speech model that is used for speech recognition.
@ -170,6 +164,15 @@ export abstract class SpeechConfig {
*/
public abstract get endpointId(): string;
/**
* Gets/Sets the endpoint ID of a customized speech model that is used for speech recognition.
* @member SpeechConfig.prototype.endpointId
* @function
* @public
* @param {string} value - The endpoint ID
*/
public abstract set endpointId(value: string);
/**
* Closes the configuration.
* @member SpeechConfig.prototype.close
@ -236,14 +239,14 @@ export class SpeechConfigImpl extends SpeechConfig {
this.privProperties.setProperty(OutputFormatPropertyName, OutputFormat[value]);
}
public set endpointId(value: string) {
this.privProperties.setProperty(PropertyId.SpeechServiceConnection_EndpointId, value);
}
public get endpointId(): string {
return this.privProperties.getProperty(PropertyId.SpeechServiceConnection_EndpointId);
}
public set endpointId(value: string) {
this.privProperties.setProperty(PropertyId.SpeechServiceConnection_EndpointId, value);
}
public setProperty(name: string | PropertyId, value: string): void {
Contracts.throwIfNullOrWhitespace(value, "value");

Просмотреть файл

@ -90,18 +90,6 @@ export class SpeechRecognizer extends Recognizer {
return this.properties.getProperty(PropertyId.SpeechServiceConnection_EndpointId, "00000000-0000-0000-0000-000000000000");
}
/**
* Sets the authorization token used to communicate with the service.
* @member SpeechRecognizer.prototype.authorizationToken
* @function
* @public
* @param {string} token - Authorization token.
*/
public set authorizationToken(token: string) {
Contracts.throwIfNullOrWhitespace(token, "token");
this.properties.setProperty(PropertyId.SpeechServiceAuthorization_Token, token);
}
/**
* Gets the authorization token used to communicate with the service.
* @member SpeechRecognizer.prototype.authorizationToken
@ -113,6 +101,18 @@ export class SpeechRecognizer extends Recognizer {
return this.properties.getProperty(PropertyId.SpeechServiceAuthorization_Token);
}
/**
* Gets/Sets the authorization token used to communicate with the service.
* @member SpeechRecognizer.prototype.authorizationToken
* @function
* @public
* @param {string} token - Authorization token.
*/
public set authorizationToken(token: string) {
Contracts.throwIfNullOrWhitespace(token, "token");
this.properties.setProperty(PropertyId.SpeechServiceAuthorization_Token, token);
}
/**
* Gets the spoken language of recognition.
* @member SpeechRecognizer.prototype.speechRecognitionLanguage

Просмотреть файл

@ -86,7 +86,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
}
/**
* Sets the authorization token.
* Gets/Sets the authorization token.
* If this is set, subscription key is ignored.
* User needs to make sure the provided authorization token is valid and not expired.
* @member SpeechTranslationConfig.prototype.authorizationToken
@ -97,9 +97,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
public abstract set authorizationToken(value: string);
/**
* Sets the authorization token.
* If this is set, subscription key is ignored.
* User needs to make sure the provided authorization token is valid and not expired.
* Gets/Sets the speech recognition language.
* @member SpeechTranslationConfig.prototype.speechRecognitionLanguage
* @function
* @public
@ -117,7 +115,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
public abstract addTargetLanguage(value: string): void;
/**
* Add a (text) target language to translate into.
* Gets the (text) target language to translate into.
* @member SpeechTranslationConfig.prototype.targetLanguages
* @function
* @public
@ -126,7 +124,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
public abstract get targetLanguages(): string[];
/**
* Returns the selected voice name.
* Gets the selected voice name.
* @member SpeechTranslationConfig.prototype.voiceName
* @function
* @public
@ -135,7 +133,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
public abstract get voiceName(): string;
/**
* Sets voice of the translated language, enable voice synthesis output.
* Gets/Sets voice of the translated language, enable voice synthesis output.
* @member SpeechTranslationConfig.prototype.voiceName
* @function
* @public
@ -176,7 +174,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
this.outputFormat = OutputFormat.Simple;
}
/**
* Sets the authorization token.
* Gets/Sets the authorization token.
* If this is set, subscription key is ignored.
* User needs to make sure the provided authorization token is valid and not expired.
* @member SpeechTranslationConfigImpl.prototype.authorizationToken
@ -191,9 +189,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
}
/**
* Sets the authorization token.
* If this is set, subscription key is ignored.
* User needs to make sure the provided authorization token is valid and not expired.
* Gets/Sets the speech recognition language.
* @member SpeechTranslationConfigImpl.prototype.speechRecognitionLanguage
* @function
* @public
@ -214,6 +210,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
}
/**
* Gets the output format
* @member SpeechTranslationConfigImpl.prototype.outputFormat
* @function
* @public
@ -223,6 +220,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
}
/**
* Gets/Sets the output format
* @member SpeechTranslationConfigImpl.prototype.outputFormat
* @function
* @public
@ -232,6 +230,17 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
}
/**
* Gets the endpoint id.
* @member SpeechTranslationConfigImpl.prototype.endpointId
* @function
* @public
*/
public get endpointId(): string {
return this.privSpeechProperties.getProperty(PropertyId.SpeechServiceConnection_EndpointId);
}
/**
* Gets/Sets the endpoint id.
* @member SpeechTranslationConfigImpl.prototype.endpointId
* @function
* @public
@ -240,14 +249,6 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
this.privSpeechProperties.setProperty(PropertyId.SpeechServiceConnection_Endpoint, value);
}
/**
* @member SpeechTranslationConfigImpl.prototype.endpointId
* @function
* @public
*/
public get endpointId(): string {
return this.privSpeechProperties.getProperty(PropertyId.SpeechServiceConnection_EndpointId);
}
/**
* Add a (text) target language to translate into.
* @member SpeechTranslationConfigImpl.prototype.addTargetLanguage
@ -264,7 +265,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
}
/**
* Add a (text) target language to translate into.
* Gets the (text) target language to translate into.
* @member SpeechTranslationConfigImpl.prototype.targetLanguages
* @function
* @public
@ -281,6 +282,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
}
/**
* Gets the voice name.
* @member SpeechTranslationConfigImpl.prototype.voiceName
* @function
* @public
@ -290,7 +292,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
}
/**
* Sets voice of the translated language, enable voice synthesis output.
* Gets/Sets the voice of the translated language, enable voice synthesis output.
* @member SpeechTranslationConfigImpl.prototype.voiceName
* @function
* @public
@ -314,19 +316,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
}
/**
* Allows for setting arbitrary properties.
* @member SpeechTranslationConfigImpl.prototype.setProperty
* @function
* @public
* @param {string} name - The name of the property.
* @param {string} value - The value of the property.
*/
public setProperty(name: string, value: string): void {
this.privSpeechProperties.setProperty(name, value);
}
/**
* Allows for retrieving arbitrary property values.
* Gets an arbitrary property value.
* @member SpeechTranslationConfigImpl.prototype.getProperty
* @function
* @public
@ -338,6 +328,18 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
return this.privSpeechProperties.getProperty(name, def);
}
/**
* Gets/Sets an arbitrary property value.
* @member SpeechTranslationConfigImpl.prototype.setProperty
* @function
* @public
* @param {string} name - The name of the property.
* @param {string} value - The value of the property.
*/
public setProperty(name: string, value: string): void {
this.privSpeechProperties.setProperty(name, value);
}
/**
* Provides access to custom properties.
* @member SpeechTranslationConfigImpl.prototype.properties

Просмотреть файл

@ -147,7 +147,7 @@ export class TranslationRecognizer extends Recognizer {
}
/**
* Sets the authorization token used to communicate with the service.
* Gets/Sets the authorization token used to communicate with the service.
* @member TranslationRecognizer.prototype.authorizationToken
* @function
* @public

Просмотреть файл

@ -0,0 +1,155 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as sdk from "../../microsoft.cognitiveservices.speech.sdk";
import { ConsoleLoggingListener, WebsocketMessageAdapter } from "../../src/common.browser/Exports";
import { Events, EventType, PlatformEvent } from "../../src/common/Exports";
import { Settings } from "../Settings";
import { WaveFileAudioInput } from "../WaveFileAudioInputStream";
import * as request from "request";
import WaitForCondition from "../Utilities";
let objsToClose: any[];
beforeAll(() => {
// override inputs, if necessary
Settings.LoadSettings();
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
});
// Test cases are run linerally, the only other mechanism to demark them in the output is to put a console line in each case and
// report the name.
beforeEach(() => {
objsToClose = [];
// tslint:disable-next-line:no-console
console.info("---------------------------------------Starting test case-----------------------------------");
// tslint:disable-next-line:no-console
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
});
afterEach(() => {
// tslint:disable-next-line:no-console
console.info("End Time: " + new Date(Date.now()).toLocaleString());
objsToClose.forEach((value: any, index: number, array: any[]) => {
if (typeof value.close === "function") {
value.close();
}
});
});
test("Non-refreshed auth token has sensible error message", (done: jest.DoneCallback) => {
// tslint:disable-next-line:no-console
console.info("Non-refreshed auth token has sensible error message");
if (!Settings.ExecuteLongRunningTestsBool) {
// tslint:disable-next-line:no-console
console.info("Skipping test.");
done();
return;
}
const req = {
headers: {
"Content-Type": "application/json",
"Ocp-Apim-Subscription-Key": Settings.SpeechSubscriptionKey,
},
url: "https://" + Settings.SpeechRegion + ".api.cognitive.microsoft.com/sts/v1.0/issueToken",
};
let authToken: string;
request.post(req, (error: any, response: request.Response, body: any) => {
authToken = body;
});
WaitForCondition(() => {
return !!authToken;
}, () => {
// Pump valid speech and then silence until at least one speech end cycle hits.
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
const s: sdk.SpeechConfig = sdk.SpeechConfig.fromAuthorizationToken(authToken, Settings.SpeechRegion);
objsToClose.push(s);
let pumpSilence: boolean = false;
let bytesSent: number = 0;
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
const p: sdk.PullAudioInputStream = sdk.AudioInputStream.createPullStream(
{
close: () => { return; },
read: (buffer: ArrayBuffer): number => {
if (pumpSilence) {
bytesSent += buffer.byteLength;
if (bytesSent >= 32000) {
bytesSent = 0;
pumpSilence = false;
}
return buffer.byteLength;
} else {
const copyArray: Uint8Array = new Uint8Array(buffer);
const start: number = bytesSent;
const end: number = buffer.byteLength > (fileBuffer.byteLength - bytesSent) ? (fileBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
copyArray.set(new Uint8Array(fileBuffer.slice(start, end)));
const readyToSend: number = (end - start) + 1;
bytesSent += readyToSend;
if (readyToSend < buffer.byteLength) {
bytesSent = 0;
pumpSilence = true;
}
return readyToSend;
}
},
});
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
const r: sdk.SpeechRecognizer = new sdk.SpeechRecognizer(s, config);
objsToClose.push(r);
let lastOffset: number = 0;
let canceled: boolean = false;
r.recognized = (o: sdk.Recognizer, e: sdk.SpeechRecognitionEventArgs) => {
try {
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.RecognizedSpeech]);
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
lastOffset = e.offset;
r.authorizationToken = "BadToken";
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
// connection is disconnected.
if ("" !== e.result.text) {
expect(e.result.text).toEqual(Settings.WaveFileText);
}
} catch (error) {
done.fail(error);
}
};
r.canceled = (o: sdk.Recognizer, e: sdk.SpeechRecognitionCanceledEventArgs): void => {
try {
expect(e.errorDetails.indexOf("Unable to contact server.")).toBeGreaterThanOrEqual(0);
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.Error]);
canceled = true;
} catch (error) {
done.fail(error);
}
};
r.startContinuousRecognitionAsync(() => {
WaitForCondition(() => (canceled), () => {
done();
});
}, (error: string) => {
done.fail(error);
});
});
}, 1000 * 60 * 20); // 20 minutes.

Просмотреть файл

@ -0,0 +1,210 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as sdk from "../../microsoft.cognitiveservices.speech.sdk";
import { ConsoleLoggingListener, WebsocketMessageAdapter } from "../../src/common.browser/Exports";
import { Events, EventType, PlatformEvent } from "../../src/common/Exports";
import { Settings } from "../Settings";
import { WaveFileAudioInput } from "../WaveFileAudioInputStream";
import * as request from "request";
import WaitForCondition from "../Utilities";
let objsToClose: any[];
beforeAll(() => {
// override inputs, if necessary
Settings.LoadSettings();
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
});
// Test cases are run linerally, the only other mechanism to demark them in the output is to put a console line in each case and
// report the name.
beforeEach(() => {
objsToClose = [];
// tslint:disable-next-line:no-console
console.info("---------------------------------------Starting test case-----------------------------------");
// tslint:disable-next-line:no-console
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
});
afterEach(() => {
// tslint:disable-next-line:no-console
console.info("End Time: " + new Date(Date.now()).toLocaleString());
objsToClose.forEach((value: any, index: number, array: any[]) => {
if (typeof value.close === "function") {
value.close();
}
});
});
test("AuthToken refresh works correctly", (done: jest.DoneCallback) => {
// tslint:disable-next-line:no-console
console.info("Name: AuthToken refresh works correctly");
if (!Settings.ExecuteLongRunningTestsBool) {
// tslint:disable-next-line:no-console
console.info("Skipping test.");
done();
return;
}
const req = {
headers: {
"Content-Type": "application/json",
"Ocp-Apim-Subscription-Key": Settings.SpeechSubscriptionKey,
},
url: "https://" + Settings.SpeechRegion + ".api.cognitive.microsoft.com/sts/v1.0/issueToken",
};
let authToken: string;
request.post(req, (error: any, response: request.Response, body: any) => {
authToken = body;
});
WaitForCondition(() => {
return !!authToken;
}, () => {
// Pump valid speech and then silence until at least one speech end cycle hits.
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
const s: sdk.SpeechConfig = sdk.SpeechConfig.fromAuthorizationToken(authToken, Settings.SpeechRegion);
objsToClose.push(s);
let pumpSilence: boolean = false;
let bytesSent: number = 0;
let streamStopped: boolean = false;
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
const p: sdk.PullAudioInputStream = sdk.AudioInputStream.createPullStream(
{
close: () => { return; },
read: (buffer: ArrayBuffer): number => {
if (pumpSilence) {
bytesSent += buffer.byteLength;
if (bytesSent >= 32000) {
bytesSent = 0;
pumpSilence = false;
}
return buffer.byteLength;
} else {
const copyArray: Uint8Array = new Uint8Array(buffer);
const start: number = bytesSent;
const end: number = buffer.byteLength > (fileBuffer.byteLength - bytesSent) ? (fileBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
copyArray.set(new Uint8Array(fileBuffer.slice(start, end)));
const readyToSend: number = (end - start) + 1;
bytesSent += readyToSend;
if (readyToSend < buffer.byteLength) {
bytesSent = 0;
pumpSilence = true;
}
return readyToSend;
}
},
});
// Close p in 20 minutes.
const endTime: number = Date.now() + (1000 * 60 * 20); // 20 min.
WaitForCondition(() => {
return Date.now() >= endTime;
}, () => {
streamStopped = true;
p.close();
});
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
const r: sdk.SpeechRecognizer = new sdk.SpeechRecognizer(s, config);
objsToClose.push(r);
// auto refresh the auth token.
const refreshAuthToken = () => {
if (canceled && !inTurn) {
return;
}
request.post(req, (error: any, response: request.Response, body: any) => {
r.authorizationToken = body;
});
setTimeout(() => {
refreshAuthToken();
}, 1000 * 60 * 9); // 9 minutes
};
refreshAuthToken();
let speechEnded: number = 0;
let lastOffset: number = 0;
let canceled: boolean = false;
let inTurn: boolean = false;
r.recognized = (o: sdk.Recognizer, e: sdk.SpeechRecognitionEventArgs) => {
try {
// The last chunk may be partial depending on where the audio was when the stream was closed.
// So just ignore it.
if (streamStopped) {
return;
}
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.RecognizedSpeech]);
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
lastOffset = e.offset;
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
// connection is disconnected.
if ("" !== e.result.text) {
expect(e.result.text).toEqual(Settings.WaveFileText);
}
} catch (error) {
done.fail(error);
}
};
r.canceled = (o: sdk.Recognizer, e: sdk.SpeechRecognitionCanceledEventArgs): void => {
try {
expect(e.errorDetails).toBeUndefined();
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.EndOfStream]);
canceled = true;
} catch (error) {
done.fail(error);
}
};
r.sessionStarted = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
inTurn = true;
});
r.sessionStopped = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
inTurn = false;
});
r.speechEndDetected = (o: sdk.Recognizer, e: sdk.RecognitionEventArgs): void => {
speechEnded++;
};
r.startContinuousRecognitionAsync(() => {
WaitForCondition(() => (canceled && !inTurn), () => {
r.stopContinuousRecognitionAsync(() => {
try {
expect(speechEnded).toEqual(1);
done();
} catch (error) {
done.fail(error);
}
}, (error: string) => {
done.fail(error);
});
});
},
(err: string) => {
done.fail(err);
});
});
}, 1000 * 60 * 25); // 25 minutes.

Просмотреть файл

@ -0,0 +1,205 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as sdk from "../../microsoft.cognitiveservices.speech.sdk";
import { ConsoleLoggingListener, WebsocketMessageAdapter } from "../../src/common.browser/Exports";
import { Events, EventType, PlatformEvent } from "../../src/common/Exports";
import { Settings } from "../Settings";
import { WaveFileAudioInput } from "../WaveFileAudioInputStream";
import * as request from "request";
import WaitForCondition from "../Utilities";
let objsToClose: any[];
beforeAll(() => {
// override inputs, if necessary
Settings.LoadSettings();
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
});
// Test cases are run linerally, the only other mechanism to demark them in the output is to put a console line in each case and
// report the name.
beforeEach(() => {
objsToClose = [];
// tslint:disable-next-line:no-console
console.info("---------------------------------------Starting test case-----------------------------------");
// tslint:disable-next-line:no-console
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
});
afterEach(() => {
// tslint:disable-next-line:no-console
console.info("End Time: " + new Date(Date.now()).toLocaleString());
objsToClose.forEach((value: any, index: number, array: any[]) => {
if (typeof value.close === "function") {
value.close();
}
});
});
const BuildSpeechConfig: () => sdk.SpeechConfig = (): sdk.SpeechConfig => {
let s: sdk.SpeechConfig;
if (undefined === Settings.SpeechEndpoint) {
s = sdk.SpeechConfig.fromSubscription(Settings.SpeechSubscriptionKey, Settings.SpeechRegion);
} else {
s = sdk.SpeechConfig.fromEndpoint(new URL(Settings.SpeechEndpoint), Settings.SpeechSubscriptionKey);
}
expect(s).not.toBeUndefined();
return s;
};
// Tests client reconnect after speech timeouts.
test("Reconnect After timeout", (done: jest.DoneCallback) => {
// tslint:disable-next-line:no-console
console.info("Name: Reconnect After timeout");
if (!Settings.ExecuteLongRunningTestsBool) {
// tslint:disable-next-line:no-console
console.info("Skipping test.");
done();
return;
}
// Pump valid speech and then silence until at least one speech end cycle hits.
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
const alternatePhraseFileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.LuisWaveFile);
let p: sdk.PullAudioInputStream;
let s: sdk.SpeechConfig;
if (undefined === Settings.SpeechTimeoutEndpoint || undefined === Settings.SpeechTimeoutKey) {
// tslint:disable-next-line:no-console
console.warn("Running timeout test against production, this will be very slow...");
s = BuildSpeechConfig();
} else {
s = sdk.SpeechConfig.fromEndpoint(new URL(Settings.SpeechTimeoutEndpoint), Settings.SpeechTimeoutKey);
}
objsToClose.push(s);
let pumpSilence: boolean = false;
let sendAlternateFile: boolean = false;
let bytesSent: number = 0;
const targetLoops: number = 500;
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
p = sdk.AudioInputStream.createPullStream(
{
close: () => { return; },
read: (buffer: ArrayBuffer): number => {
if (pumpSilence) {
bytesSent += buffer.byteLength;
if (bytesSent >= 16000) {
bytesSent = 0;
pumpSilence = false;
}
return buffer.byteLength;
} else {
// Alternate between the two files with different phrases in them.
const sendBuffer: ArrayBuffer = sendAlternateFile ? alternatePhraseFileBuffer : fileBuffer;
const copyArray: Uint8Array = new Uint8Array(buffer);
const start: number = bytesSent;
const end: number = buffer.byteLength > (sendBuffer.byteLength - bytesSent) ? (sendBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
copyArray.set(new Uint8Array(sendBuffer.slice(start, end)));
const readyToSend: number = (end - start) + 1;
bytesSent += readyToSend;
if (readyToSend < buffer.byteLength) {
bytesSent = 0;
pumpSilence = true;
sendAlternateFile = !sendAlternateFile;
}
return readyToSend;
}
},
});
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
const r: sdk.SpeechRecognizer = new sdk.SpeechRecognizer(s, config);
objsToClose.push(r);
let speechEnded: number = 0;
let lastOffset: number = 0;
let recogCount: number = 0;
let canceled: boolean = false;
let inTurn: boolean = false;
let alternatePhrase: boolean = false;
r.recognized = (o: sdk.Recognizer, e: sdk.SpeechRecognitionEventArgs) => {
try {
// If the target number of loops has been seen already, don't check as the audio being sent could have been clipped randomly during a phrase,
// and failing because of that isn't warranted.
if (recogCount <= targetLoops) {
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.RecognizedSpeech]);
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
lastOffset = e.offset;
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
// connection is disconnected.
if ("" !== e.result.text) {
if (alternatePhrase) {
expect(e.result.text).toEqual(Settings.LuisWavFileText);
} else {
expect(e.result.text).toEqual(Settings.WaveFileText);
}
alternatePhrase = !alternatePhrase;
}
if (recogCount++ >= targetLoops) {
p.close();
}
}
} catch (error) {
done.fail(error);
}
};
r.canceled = (o: sdk.Recognizer, e: sdk.SpeechRecognitionCanceledEventArgs): void => {
try {
expect(e.errorDetails).toBeUndefined();
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.EndOfStream]);
canceled = true;
} catch (error) {
done.fail(error);
}
};
r.sessionStarted = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
inTurn = true;
});
r.sessionStopped = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
inTurn = false;
});
r.speechEndDetected = (o: sdk.Recognizer, e: sdk.RecognitionEventArgs): void => {
speechEnded++;
};
r.startContinuousRecognitionAsync(() => {
WaitForCondition(() => (canceled && !inTurn), () => {
r.stopContinuousRecognitionAsync(() => {
try {
expect(speechEnded).toEqual(1);
done();
} catch (error) {
done.fail(error);
}
}, (error: string) => {
done.fail(error);
});
});
},
(err: string) => {
done.fail(err);
});
}, 1000 * 60 * 12);

Просмотреть файл

@ -0,0 +1,200 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as sdk from "../../microsoft.cognitiveservices.speech.sdk";
import { ConsoleLoggingListener, WebsocketMessageAdapter } from "../../src/common.browser/Exports";
import { Events, EventType, PlatformEvent } from "../../src/common/Exports";
import { Settings } from "../Settings";
import { WaveFileAudioInput } from "../WaveFileAudioInputStream";
import * as request from "request";
import WaitForCondition from "../Utilities";
let objsToClose: any[];
beforeAll(() => {
// override inputs, if necessary
Settings.LoadSettings();
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
});
// Test cases are run linerally, the only other mechanism to demark them in the output is to put a console line in each case and
// report the name.
beforeEach(() => {
objsToClose = [];
// tslint:disable-next-line:no-console
console.info("---------------------------------------Starting test case-----------------------------------");
// tslint:disable-next-line:no-console
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
});
afterEach(() => {
// tslint:disable-next-line:no-console
console.info("End Time: " + new Date(Date.now()).toLocaleString());
objsToClose.forEach((value: any, index: number, array: any[]) => {
if (typeof value.close === "function") {
value.close();
}
});
});
const BuildSpeechConfig: () => sdk.SpeechTranslationConfig = (): sdk.SpeechTranslationConfig => {
const s: sdk.SpeechTranslationConfig = sdk.SpeechTranslationConfig.fromSubscription(Settings.SpeechSubscriptionKey, Settings.SpeechRegion);
expect(s).not.toBeUndefined();
return s;
};
// Tests client reconnect after speech timeouts.
test("Reconnect After timeout", (done: jest.DoneCallback) => {
// tslint:disable-next-line:no-console
console.info("Name: Reconnect After timeout");
if (!Settings.ExecuteLongRunningTestsBool) {
// tslint:disable-next-line:no-console
console.info("Skipping test.");
done();
return;
}
// Pump valid speech and then silence until at least one speech end cycle hits.
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
const alternatePhraseFileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.LuisWaveFile);
let p: sdk.PullAudioInputStream;
let s: sdk.SpeechTranslationConfig;
if (undefined === Settings.SpeechTimeoutEndpoint || undefined === Settings.SpeechTimeoutKey) {
// tslint:disable-next-line:no-console
console.warn("Running timeout test against production, this will be very slow...");
s = BuildSpeechConfig();
} else {
s = sdk.SpeechTranslationConfig.fromEndpoint(new URL(Settings.SpeechTimeoutEndpoint), Settings.SpeechTimeoutKey);
}
objsToClose.push(s);
s.addTargetLanguage(Settings.WaveFileLanguage);
s.speechRecognitionLanguage = Settings.WaveFileLanguage;
let pumpSilence: boolean = false;
let sendAlternateFile: boolean = false;
let bytesSent: number = 0;
const targetLoops: number = 250;
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
p = sdk.AudioInputStream.createPullStream(
{
close: () => { return; },
read: (buffer: ArrayBuffer): number => {
if (pumpSilence) {
bytesSent += buffer.byteLength;
if (bytesSent >= 16000) {
bytesSent = 0;
pumpSilence = false;
}
return buffer.byteLength;
} else {
// Alternate between the two files with different phrases in them.
const sendBuffer: ArrayBuffer = sendAlternateFile ? alternatePhraseFileBuffer : fileBuffer;
const copyArray: Uint8Array = new Uint8Array(buffer);
const start: number = bytesSent;
const end: number = buffer.byteLength > (sendBuffer.byteLength - bytesSent) ? (sendBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
copyArray.set(new Uint8Array(sendBuffer.slice(start, end)));
const readyToSend: number = (end - start) + 1;
bytesSent += readyToSend;
if (readyToSend < buffer.byteLength) {
bytesSent = 0;
pumpSilence = true;
sendAlternateFile = !sendAlternateFile;
}
return readyToSend;
}
},
});
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
const r: sdk.TranslationRecognizer = new sdk.TranslationRecognizer(s, config);
objsToClose.push(r);
let speechEnded: number = 0;
let lastOffset: number = 0;
let recogCount: number = 0;
let canceled: boolean = false;
let inTurn: boolean = false;
let alternatePhrase: boolean = false;
r.recognized = (o: sdk.Recognizer, e: sdk.TranslationRecognitionEventArgs) => {
try {
// If the target number of loops has been seen already, don't check as the audio being sent could have been clipped randomly during a phrase,
// and failing because of that isn't warranted.
if (recogCount <= targetLoops) {
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.TranslatedSpeech]);
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
lastOffset = e.offset;
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
// connection is disconnected.
if ("" !== e.result.text) {
if (alternatePhrase) {
expect(e.result.text).toEqual(Settings.LuisWavFileText);
} else {
expect(e.result.text).toEqual(Settings.WaveFileText);
}
alternatePhrase = !alternatePhrase;
}
if (recogCount++ >= targetLoops) {
p.close();
}
}
} catch (error) {
done.fail(error);
}
};
r.canceled = (o: sdk.Recognizer, e: sdk.TranslationRecognitionCanceledEventArgs): void => {
try {
expect(e.errorDetails).toBeUndefined();
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.EndOfStream]);
canceled = true;
} catch (error) {
done.fail(error);
}
};
r.sessionStarted = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
inTurn = true;
});
r.sessionStopped = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
inTurn = false;
});
r.speechEndDetected = (o: sdk.Recognizer, e: sdk.RecognitionEventArgs): void => {
speechEnded++;
};
r.startContinuousRecognitionAsync(() => {
WaitForCondition(() => (canceled && !inTurn), () => {
r.stopContinuousRecognitionAsync(() => {
try {
expect(speechEnded).toEqual(1);
done();
} catch (error) {
done.fail(error);
}
}, (error: string) => {
done.fail(error);
});
});
},
(err: string) => {
done.fail(err);
});
}, 1000 * 60 * 12);

Просмотреть файл

@ -24,6 +24,12 @@ export class Settings {
public static InputDir: string = "tests/input/audio/";
public static ExecuteLongRunningTests: string = "false";
public static get ExecuteLongRunningTestsBool(): boolean {
return "false" !== this.ExecuteLongRunningTests;
}
/*
* The intent behing this setting is that at test execution time the WaveFile below will contain speech
* that the LUIS app above will recognize as an intent with this ID.

Просмотреть файл

@ -11,6 +11,8 @@ import { Settings } from "./Settings";
import { WaveFileAudioInput } from "./WaveFileAudioInputStream";
import * as fs from "fs";
import * as request from "request";
import { setTimeout } from "timers";
import { ByteBufferAudioFile } from "./ByteBufferAudioFile";
import WaitForCondition from "./Utilities";
@ -1606,132 +1608,6 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
done.fail(err);
});
}, 35000);
// Tests client reconnect after speech timeouts.
test.skip("Reconnect After timeout", (done: jest.DoneCallback) => {
// tslint:disable-next-line:no-console
console.info("Name: Reconnect After timeout");
// Pump valid speech and then silence until at least one speech end cycle hits.
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
let p: sdk.PullAudioInputStream;
const bigFileBuffer: Uint8Array = new Uint8Array(32 * 1024 * 30); // ~30 seconds.
let s: sdk.SpeechConfig;
if (undefined === Settings.SpeechTimeoutEndpoint || undefined === Settings.SpeechTimeoutKey) {
// tslint:disable-next-line:no-console
console.warn("Running timeout test against production, this will be very slow...");
s = BuildSpeechConfig();
} else {
s = sdk.SpeechConfig.fromEndpoint(new URL(Settings.SpeechTimeoutEndpoint), Settings.SpeechTimeoutKey);
}
objsToClose.push(s);
let pumpSilence: boolean = false;
let bytesSent: number = 0;
const targetLoops: number = 250;
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
p = sdk.AudioInputStream.createPullStream(
{
close: () => { return; },
read: (buffer: ArrayBuffer): number => {
if (pumpSilence) {
bytesSent += buffer.byteLength;
if (bytesSent >= 8000) {
bytesSent = 0;
pumpSilence = false;
}
return buffer.byteLength;
} else {
const copyArray: Uint8Array = new Uint8Array(buffer);
const start: number = bytesSent;
const end: number = buffer.byteLength > (fileBuffer.byteLength - bytesSent) ? (fileBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
copyArray.set(new Uint8Array(fileBuffer.slice(start, end)));
const readyToSend: number = (end - start) + 1;
bytesSent += readyToSend;
if (readyToSend < buffer.byteLength) {
bytesSent = 0;
pumpSilence = true;
}
return readyToSend;
}
},
});
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
const r: sdk.SpeechRecognizer = new sdk.SpeechRecognizer(s, config);
objsToClose.push(r);
let speechEnded: number = 0;
let lastOffset: number = 0;
let recogCount: number = 0;
let canceled: boolean = false;
let inTurn: boolean = false;
r.recognized = (o: sdk.Recognizer, e: sdk.SpeechRecognitionEventArgs) => {
try {
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.RecognizedSpeech]);
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
lastOffset = e.offset;
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
// connection is disconnected.
if ("" !== e.result.text) {
expect(e.result.text).toEqual(Settings.WaveFileText);
}
if (recogCount++ > targetLoops) {
p.close();
}
} catch (error) {
done.fail(error);
}
};
r.canceled = (o: sdk.Recognizer, e: sdk.SpeechRecognitionCanceledEventArgs): void => {
try {
expect(e.errorDetails).toBeUndefined();
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.EndOfStream]);
canceled = true;
} catch (error) {
done.fail(error);
}
};
r.sessionStarted = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
inTurn = true;
});
r.sessionStopped = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
inTurn = false;
});
r.speechEndDetected = (o: sdk.Recognizer, e: sdk.RecognitionEventArgs): void => {
speechEnded++;
};
r.startContinuousRecognitionAsync(() => {
WaitForCondition(() => (canceled && !inTurn), () => {
r.stopContinuousRecognitionAsync(() => {
try {
expect(speechEnded).toEqual(1);
done();
} catch (error) {
done.fail(error);
}
}, (error: string) => {
done.fail(error);
});
});
},
(err: string) => {
done.fail(err);
});
}, 35000);
});
test("Push Stream Async", (done: jest.DoneCallback) => {
@ -1772,7 +1648,7 @@ test("Push Stream Async", (done: jest.DoneCallback) => {
(error: string) => {
done.fail(error);
});
});
}, 10000);
test("Bad DataType for PushStreams results in error", (done: jest.DoneCallback) => {
// tslint:disable-next-line:no-console

Просмотреть файл

@ -632,7 +632,7 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
});
}, 10000);
test.skip("MultiPhrase", (done: jest.DoneCallback) => {
test("MultiPhrase", (done: jest.DoneCallback) => {
// tslint:disable-next-line:no-console
console.info("Name: MultiPhrase");
const s: sdk.SpeechTranslationConfig = BuildSpeechConfig();
@ -646,9 +646,11 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
const p: sdk.PushAudioInputStream = sdk.AudioInputStream.createPushStream();
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
const numPhrases: number = 3;
const silentBuffer: ArrayBuffer = new ArrayBuffer(32000);
for (let i: number = 0; i < 3; i++) {
p.write(f);
p.write(silentBuffer);
}
p.close();
@ -686,18 +688,13 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
let inTurn: boolean = false;
r.canceled = ((o: sdk.Recognizer, e: sdk.TranslationRecognitionCanceledEventArgs) => {
try {
switch (e.reason) {
case sdk.CancellationReason.Error:
done.fail(e.errorDetails);
break;
case sdk.CancellationReason.EndOfStream:
expect(synthCount).toEqual(numPhrases);
canceled = true;
break;
}
} catch (error) {
done.fail(error);
switch (e.reason) {
case sdk.CancellationReason.Error:
done.fail(e.errorDetails);
break;
case sdk.CancellationReason.EndOfStream:
canceled = true;
break;
}
});
@ -718,6 +715,7 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
for (let i: number = 0; i < synthFragmentCount; i++) {
p.write(rEvents[i]);
p.write(silentBuffer);
}
p.close();
@ -764,8 +762,14 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
WaitForCondition(() => (canceled && !inTurn),
() => {
r2.stopContinuousRecognitionAsync(() => {
expect(numEvents).toEqual(numPhrases);
done();
try {
expect(synthCount).toEqual(numPhrases);
expect(numEvents).toEqual(numPhrases);
done();
} catch (error) {
done.fail(error);
}
}, (error: string) => {
done.fail(error);
});

Просмотреть файл

@ -1,11 +1,11 @@
{
"compilerOptions": {
"module": "commonjs",
"preserveConstEnums": true,
"sourceMap": true,
"target": "ES5",
"declaration": true,
"noImplicitAny": true,
"removeComments": false
"module": "commonjs",
"preserveConstEnums": true,
"sourceMap": true,
"target": "ES5",
"declaration": true,
"noImplicitAny": true,
"removeComments": false
}
}
}

Просмотреть файл

@ -48,4 +48,4 @@
"secrets/*.ts"
]
}
}
}