├── src
├── assets
│ ├── .gitkeep
│ └── img
│ │ └── loading.svg
├── favicon.ico
├── tsconfig.app.json
├── tsconfig.spec.json
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── app
│ ├── components
│ │ ├── index.ts
│ │ └── dialog-alert
│ │ │ ├── dialog-alert.component.html
│ │ │ └── dialog-alert.component.ts
│ ├── models
│ │ ├── IEvent.ts
│ │ ├── index.ts
│ │ ├── vision.ts
│ │ └── nlp.ts
│ ├── app.component.html
│ ├── app.component.ts
│ ├── pages
│ │ ├── index.ts
│ │ └── widgets
│ │ │ ├── widgets.component.scss
│ │ │ ├── widgets.component.spec.ts
│ │ │ ├── widgets.component.html
│ │ │ └── widgets.component.ts
│ ├── services
│ │ ├── index.ts
│ │ ├── web-synth
│ │ │ ├── web-synth.service.spec.ts
│ │ │ └── web-synth.service.ts
│ │ ├── web-speech
│ │ │ ├── web-speech.service.spec.ts
│ │ │ └── web-speech.service.ts
│ │ ├── gcloud-speech
│ │ │ ├── gcloud-speech.service.spec.ts
│ │ │ └── gcloud-speech.service.ts
│ │ ├── gcloud-nlp
│ │ │ ├── gcloud-nlp.service.ts
│ │ │ └── gcloud-nlp.service.spec.ts
│ │ └── gcloud-vision
│ │ │ ├── gcloud-vision.service.ts
│ │ │ └── gcloud-vision.service.spec.ts
│ ├── app.component.scss
│ ├── app-routing.module.ts
│ ├── app.component.spec.ts
│ └── app.module.ts
├── typings.d.ts
├── styles.scss
├── main.ts
├── index.html
├── test.ts
└── polyfills.ts
├── e2e
├── tsconfig.e2e.json
├── app.e2e-spec.ts
└── widgets.po.ts
├── tsconfig.json
├── speech-server
├── tsconfig.json
├── src
│ ├── environment.ts
│ └── server.ts
└── package.json
├── NOTICE
├── .gitignore
├── karma.conf.js
├── protractor.conf.js
├── .angular-cli.json
├── package.json
├── tslint.json
└── README.md
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gridcellcoder/cloud-speech-and-vision-demos/HEAD/src/favicon.ico
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es6",
7 | "types": [
8 | "jasmine",
9 | "node"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "es2015",
6 | "baseUrl": "",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "baseUrl": "",
8 | "types": [
9 | "jasmine",
10 | "node"
11 | ]
12 | },
13 | "files": [
14 | "test.ts"
15 | ],
16 | "include": [
17 | "**/*.spec.ts",
18 | "**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | speechServerUrl: 'ws://localhost:8000',
4 |
5 | gCloudProjectId: 'ENTER YOUR GLCOUD PROJECT ID HERE',
6 | gCloudProjectApiKey: 'ENTER YOUR OWN API KEY HERE',
7 |
8 | gCloudNLPApiUrl: 'https://language.googleapis.com/v1/documents:annotateText',
9 | gCloudVisionApiUrl: 'https://vision.googleapis.com/v1/images:annotate',
10 | };
11 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "baseUrl": "src",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "moduleResolution": "node",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2016",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/speech-server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "baseUrl": "src",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "moduleResolution": "node",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2016",
17 | "dom"
18 | ]
19 | },
20 | "include": [
21 | "src/**/*.ts"
22 | ],
23 | "exclude": [
24 | "node_modules"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017. GridCell Ltd
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | **/dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | **/node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | testem.log
34 | /typings
35 |
36 | # e2e
37 | /e2e/*.js
38 | /e2e/*.map
39 |
40 | # System Files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/src/app/components/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export * from './dialog-alert/dialog-alert.component';
18 |
--------------------------------------------------------------------------------
/speech-server/src/environment.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | export const environment = {
17 | gCloudProjectId: ''
18 | };
19 |
--------------------------------------------------------------------------------
/src/app/models/IEvent.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export interface IEvent {
18 | type: string;
19 | value: string;
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/app/models/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export * from './IEvent';
18 | export * from './nlp';
19 | export * from './vision';
20 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false,
8 | speechServerUrl: 'ws://localhost:8000',
9 |
10 | gCloudProjectId: 'ENTER YOUR GLCOUD PROJECT ID HERE',
11 | gCloudProjectApiKey: 'ENTER YOUR OWN API KEY HERE',
12 |
13 | gCloudNLPApiUrl: 'https://language.googleapis.com/v1/documents:annotateText',
14 | gCloudVisionApiUrl: 'https://vision.googleapis.com/v1/images:annotate',
15 | };
16 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /* SystemJS module definition */
18 | declare var module: NodeModule;
19 | interface NodeModule {
20 | id: string;
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { Component } from '@angular/core';
18 |
19 | @Component({
20 | selector: 'app-root',
21 | templateUrl: './app.component.html'
22 | })
23 | export class AppComponent { }
24 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | @import '~@angular/material/prebuilt-themes/deeppurple-amber.css';
17 |
18 | $gray: #C6C6C5;
19 |
20 | @import './app/app.component.scss';
21 | @import './app/pages/widgets/widgets.component.scss';
22 |
--------------------------------------------------------------------------------
/src/app/pages/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright 2017 GridCell Ltd
3 |
4 | Licensed to the Apache Software Foundation (ASF) under one
5 | or more contributor license agreements. See the NOTICE file
6 | distributed with this work for additional information
7 | regarding copyright ownership. The ASF licenses this file
8 | to you under the Apache License, Version 2.0 (the
9 | "License"); you may not use this file except in compliance
10 | with the License. You may obtain a copy of the License at
11 |
12 | http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Unless required by applicable law or agreed to in writing,
15 | software distributed under the License is distributed on an
16 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | KIND, either express or implied. See the License for the
18 | specific language governing permissions and limitations
19 | under the License.
20 | */
21 |
22 | export * from './widgets/widgets.component';
23 |
--------------------------------------------------------------------------------
/src/app/services/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export * from './web-speech/web-speech.service';
18 | export * from './web-synth/web-synth.service';
19 | export * from './gcloud-speech/gcloud-speech.service';
20 | export * from './gcloud-nlp/gcloud-nlp.service';
21 | export * from './gcloud-vision/gcloud-vision.service';
22 |
--------------------------------------------------------------------------------
/speech-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "google-speech-demo-server",
3 | "version": "0.0.1",
4 | "description": "Speech Server",
5 | "license": "Apache License, Version 2.0",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/gridcellcoder/google-speech-demos/speech-server"
9 | },
10 | "author": "GridCell",
11 | "contributors": [
12 | "Nuno Freire
16 |
17 |
18 |
19 |
20 | Gridcell Google Speech Demo
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/app/components/dialog-alert/dialog-alert.component.html:
--------------------------------------------------------------------------------
1 |
16 |
{{title}}
17 |
{{text}}
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | body {
17 | .loading-animation {
18 | width: 100%;
19 | line-height: 192px;
20 | text-align: center !important;
21 | vertical-align: middle;
22 | }
23 |
24 | .fill-space {
25 | flex: 1 1 auto;
26 | }
27 |
28 | app-root {
29 | .page {
30 | height: 100vh;
31 | padding: 15px;
32 | background-color: $gray;
33 | overflow: auto;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { NgModule } from '@angular/core';
18 | import { RouterModule, Routes } from '@angular/router';
19 | import { PageWidgetsComponent } from './pages/index';
20 |
21 | const routes: Routes = [
22 | { path: '**', component: PageWidgetsComponent },
23 | ];
24 |
25 | @NgModule({
26 | imports: [ RouterModule.forRoot(routes, { useHash: true }) ],
27 | exports: [ RouterModule ]
28 | })
29 | export class AppRoutingModule { }
30 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright 2017 GridCell Ltd
3 |
4 | Licensed to the Apache Software Foundation (ASF) under one
5 | or more contributor license agreements. See the NOTICE file
6 | distributed with this work for additional information
7 | regarding copyright ownership. The ASF licenses this file
8 | to you under the Apache License, Version 2.0 (the
9 | "License"); you may not use this file except in compliance
10 | with the License. You may obtain a copy of the License at
11 |
12 | http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Unless required by applicable law or agreed to in writing,
15 | software distributed under the License is distributed on an
16 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | KIND, either express or implied. See the License for the
18 | specific language governing permissions and limitations
19 | under the License.
20 | */
21 |
22 | import { PageWidgetsComponent } from './widgets.po';
23 |
24 | describe('gridcell-gcloud App', () => {
25 | let page: PageWidgetsComponent;
26 |
27 | beforeEach(() => {
28 | page = new PageWidgetsComponent();
29 | page.navigateTo();
30 | });
31 |
32 | it('should have all the right widgets', () => {
33 | page.shouldHaveAllWidgets();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/app/components/dialog-alert/dialog-alert.component.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { Component, OnInit, Inject } from '@angular/core';
18 | import { MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA } from '@angular/material';
19 |
20 | @Component({
21 | selector: 'dialog-alert',
22 | templateUrl: 'dialog-alert.component.html',
23 | })
24 | export class DialogAlertComponent {
25 | title = 'Alert'
26 | text = 'Oops! Something wrong happened...';
27 |
28 | constructor(public dialogRef: MdDialogRef,
29 | @Inject(MD_DIALOG_DATA) private data: any) {
30 | if (data) {
31 | this.title = data.title;
32 | this.text = data.text;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { TestBed, async } from '@angular/core/testing';
18 | import { RouterTestingModule } from '@angular/router/testing';
19 |
20 | import { AppComponent } from './app.component';
21 |
22 | describe('AppComponent', () => {
23 | beforeEach(async(() => {
24 | TestBed.configureTestingModule({
25 | declarations: [
26 | AppComponent
27 | ],
28 | imports: [
29 | RouterTestingModule,
30 | ]
31 | }).compileComponents();
32 | }));
33 |
34 | it('should create the app', async(() => {
35 | const fixture = TestBed.createComponent(AppComponent);
36 | const app = fixture.debugElement.componentInstance;
37 | expect(app).toBeTruthy();
38 | }));
39 | });
40 |
--------------------------------------------------------------------------------
/src/app/models/vision.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export enum VisionFeatureType {
18 | // TYPE_UNSPECIFIED = 0,
19 | FACE_DETECTION = 1,
20 | LANDMARK_DETECTION = 2,
21 | LOGO_DETECTION = 3,
22 | LABEL_DETECTION = 4,
23 | TEXT_DETECTION = 5,
24 | DOCUMENT_TEXT_DETECTION = 6,
25 | SAFE_SEARCH_DETECTION = 7,
26 | IMAGE_PROPERTIES = 8,
27 | CROP_HINTS = 9,
28 | WEB_DETECTION = 10
29 | }
30 |
31 | export class VisionAnnotateRequest {
32 | requests: VisionRequest[];
33 | }
34 |
35 | export class VisionRequest {
36 | image: VisionImageContent;
37 | features: VisionFeature[];
38 | }
39 |
40 | export class VisionImageContent {
41 | content: string;
42 | // source: any;
43 | }
44 |
45 | export class VisionFeature {
46 | type: VisionFeatureType;
47 | maxResults: number;
48 | }
49 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // Protractor configuration file, see link for more information
18 | // https://github.com/angular/protractor/blob/master/lib/config.ts
19 |
20 | const { SpecReporter } = require('jasmine-spec-reporter');
21 |
22 | exports.config = {
23 | allScriptsTimeout: 11000,
24 | specs: [
25 | './e2e/**/*.e2e-spec.ts'
26 | ],
27 | capabilities: {
28 | 'browserName': 'chrome'
29 | },
30 | directConnect: true,
31 | baseUrl: 'http://localhost:4200/',
32 | framework: 'jasmine',
33 | jasmineNodeOpts: {
34 | showColors: true,
35 | defaultTimeoutInterval: 30000,
36 | print: function() {}
37 | },
38 | onPrepare() {
39 | require('ts-node').register({
40 | project: 'e2e/tsconfig.e2e.json'
41 | });
42 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "cloud-speech-and-vision-demos"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "polyfills": "polyfills.ts",
17 | "test": "test.ts",
18 | "tsconfig": "tsconfig.app.json",
19 | "testTsconfig": "tsconfig.spec.json",
20 | "prefix": "app",
21 | "styles": [
22 | "../node_modules/bootstrap/dist/css/bootstrap.css",
23 | "../node_modules/font-awesome/scss/font-awesome.scss",
24 | "../node_modules/angular2-busy/build/style/busy.css",
25 | "styles.scss"
26 | ],
27 | "scripts": [],
28 | "environmentSource": "environments/environment.ts",
29 | "environments": {
30 | "dev": "environments/environment.ts",
31 | "prod": "environments/environment.prod.ts"
32 | }
33 | }
34 | ],
35 | "e2e": {
36 | "protractor": {
37 | "config": "./protractor.conf.js"
38 | }
39 | },
40 | "lint": [
41 | {
42 | "project": "src/tsconfig.app.json"
43 | },
44 | {
45 | "project": "src/tsconfig.spec.json"
46 | },
47 | {
48 | "project": "e2e/tsconfig.e2e.json"
49 | }
50 | ],
51 | "test": {
52 | "karma": {
53 | "config": "./karma.conf.js"
54 | }
55 | },
56 | "defaults": {
57 | "styleExt": "scss",
58 | "component": {}
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/app/services/web-synth/web-synth.service.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { inject, fakeAsync, tick, TestBed } from '@angular/core/testing';
18 | import { Observable } from 'rxjs/Observable';
19 | import { WebSynthService } from '../index';
20 |
21 | describe('WebSynthService', () => {
22 | let fakeTestObject: any = { dummy: 1 };
23 |
24 | beforeEach(() => {
25 | TestBed.configureTestingModule({
26 | providers: [
27 | WebSynthService,
28 | ]
29 | });
30 | });
31 |
32 | describe('start', () => {
33 | it('should start recognizing and return observable',
34 | inject([WebSynthService], fakeAsync((service: WebSynthService) => {
35 | let spy = spyOn((service).engine, 'start');
36 | let observable = service.start();
37 | tick();
38 |
39 | expect(spy).toHaveBeenCalled();
40 | expect(observable).toEqual(jasmine.any(Observable));
41 | }))
42 | );
43 | });
44 |
45 | });
46 |
--------------------------------------------------------------------------------
/src/app/services/web-speech/web-speech.service.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { inject, fakeAsync, tick, TestBed } from '@angular/core/testing';
18 | import { Observable } from 'rxjs/Observable';
19 | import { WebSpeechService } from '../index';
20 |
21 | describe('WebSpeechService', () => {
22 | let fakeTestObject: any = { dummy: 1 };
23 |
24 | beforeEach(() => {
25 | TestBed.configureTestingModule({
26 | providers: [
27 | WebSpeechService,
28 | ]
29 | });
30 | });
31 |
32 | describe('start', () => {
33 | it('should start recognizing and return observable',
34 | inject([WebSpeechService], fakeAsync((service: WebSpeechService) => {
35 | let spy = spyOn((service).engine, 'start');
36 | let observable = service.start();
37 | tick();
38 |
39 | expect(spy).toHaveBeenCalled();
40 | expect(observable).toEqual(jasmine.any(Observable));
41 | }))
42 | );
43 | });
44 |
45 | });
46 |
--------------------------------------------------------------------------------
/src/app/pages/widgets/widgets.component.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | page-widgets {
17 | a {
18 | color: initial;
19 | text-decoration: underline;
20 | }
21 |
22 | .card {
23 | min-height: 128px;
24 | margin-bottom: 15px;
25 | }
26 |
27 | .title {
28 | margin-right: 15px;
29 | }
30 |
31 | .inline {
32 | display: inline-block;
33 | }
34 |
35 | .spacer {
36 | height: 8px;
37 | }
38 |
39 | .ng-busy-backdrop {
40 | left: 15px;
41 | right: 15px;
42 | bottom: 15px;
43 | }
44 |
45 | .mat-input-container {
46 | width: 100%;
47 | }
48 |
49 | .mat-chip:not(.mat-basic-chip) {
50 | font-size: 12px !important;
51 | line-height: 14px !important;
52 | margin-bottom: 4px;
53 | }
54 |
55 | agm-map {
56 | height: 300px;
57 | }
58 |
59 | .select-features {
60 | min-width: 128px;
61 | margin-right: 16px;
62 | }
63 |
64 | .bg-warning {
65 | font-size: 12px;
66 | padding: 4px;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/e2e/widgets.po.ts:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright 2017 GridCell Ltd
3 |
4 | Licensed to the Apache Software Foundation (ASF) under one
5 | or more contributor license agreements. See the NOTICE file
6 | distributed with this work for additional information
7 | regarding copyright ownership. The ASF licenses this file
8 | to you under the Apache License, Version 2.0 (the
9 | "License"); you may not use this file except in compliance
10 | with the License. You may obtain a copy of the License at
11 |
12 | http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Unless required by applicable law or agreed to in writing,
15 | software distributed under the License is distributed on an
16 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | KIND, either express or implied. See the License for the
18 | specific language governing permissions and limitations
19 | under the License.
20 | */
21 |
22 | import { browser, by, element } from 'protractor';
23 |
24 | export class PageWidgetsComponent {
25 | readonly titles: string[] = [
26 | 'WEB SPEECH API', 'GCLOUD SPEECH API', 'GCLOUD VISION API'
27 | ];
28 |
29 | navigateTo() {
30 | return browser.get('/');
31 | }
32 |
33 | getAllWidgetsTitles() {
34 | return element(by.css('page-widgets')).all(by.css('.title'));
35 | }
36 |
37 | shouldHaveAllWidgets() {
38 | let widgets = this.getAllWidgetsTitles();
39 | widgets.count().then((count) => {
40 | expect(count).toEqual(this.titles.length);
41 | });
42 |
43 | widgets.each((title, index) => {
44 | title.getText().then((text: string) => {
45 | expect(this.titles).toContain(text);
46 | });
47 | })
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/app/services/gcloud-speech/gcloud-speech.service.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { inject, fakeAsync, tick, TestBed } from '@angular/core/testing';
18 | import { Observable } from 'rxjs/Observable';
19 | import { GCloudSpeechService } from '../index';
20 |
21 | describe('GCloudNLPService', () => {
22 | let fakeTestObject: any = { dummy: 1 };
23 |
24 | beforeEach(() => {
25 | TestBed.configureTestingModule({
26 | providers: [
27 | GCloudSpeechService,
28 | ]
29 | });
30 | });
31 |
32 | describe('start', () => {
33 | it('should start recognizing and return observable',
34 | inject([GCloudSpeechService], fakeAsync((service: GCloudSpeechService) => {
35 | let spy = spyOn(service, 'getUserMedia');
36 | let observable = service.start();
37 | tick();
38 |
39 | expect(spy).toHaveBeenCalled();
40 | expect(observable).toEqual(jasmine.any(Observable));
41 | expect(service.isRecognizing()).toBeTruthy();
42 | }))
43 | );
44 | });
45 |
46 | });
47 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
18 |
19 | import 'zone.js/dist/long-stack-trace-zone';
20 | import 'zone.js/dist/proxy.js';
21 | import 'zone.js/dist/sync-test';
22 | import 'zone.js/dist/jasmine-patch';
23 | import 'zone.js/dist/async-test';
24 | import 'zone.js/dist/fake-async-test';
25 | import { getTestBed } from '@angular/core/testing';
26 | import {
27 | BrowserDynamicTestingModule,
28 | platformBrowserDynamicTesting
29 | } from '@angular/platform-browser-dynamic/testing';
30 |
31 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
32 | declare const __karma__: any;
33 | declare const require: any;
34 |
35 | // Prevent Karma from running prematurely.
36 | __karma__.loaded = function () {};
37 |
38 | // First, initialize the Angular testing environment.
39 | getTestBed().initTestEnvironment(
40 | BrowserDynamicTestingModule,
41 | platformBrowserDynamicTesting()
42 | );
43 | // Then we find all the tests.
44 | const context = require.context('./', true, /\.spec\.ts$/);
45 | // And load the modules.
46 | context.keys().map(context);
47 | // Finally, start Karma to run the tests.
48 | __karma__.start();
49 |
--------------------------------------------------------------------------------
/src/app/models/nlp.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export enum NLPDocumentType {
18 | TYPE_UNSPECIFIED,
19 | PLAIN_TEXT,
20 | HTML
21 | }
22 |
23 | export enum NLPEncodingType {
24 | NONE,
25 | UTF8,
26 | UTF16,
27 | UTF32
28 | }
29 |
30 | export class NLPDocument {
31 | type: NLPDocumentType;
32 | language: string;
33 | content: string;
34 | }
35 |
36 | export class NLPFeatures {
37 | extractSyntax: boolean;
38 | extractEntities: boolean;
39 | extractDocumentSentiment: boolean;
40 | }
41 |
42 | export class NLPSpeechAnalysis {
43 | json: string;
44 | tags: NLPSpeechAnalysisTag[];
45 | sentiment: NLPSpeechAnalysisSentiment;
46 |
47 | constructor(data: any) {
48 | this.json = JSON.stringify(data, null, 2);
49 | this.sentiment = {
50 | score: data.documentSentiment.score,
51 | magnitude: data.documentSentiment.magnitude
52 | }
53 | this.tags = [];
54 |
55 | for (let entity of data.entities) {
56 | if (entity.name !== 'name') {
57 | this.tags.push({
58 | name: entity.name,
59 | type: entity.type,
60 | wikipedia_url: entity.metadata.wikipedia_url
61 | });
62 | }
63 | }
64 | }
65 | }
66 |
67 | export class NLPSpeechAnalysisTag {
68 | name: string;
69 | type: string;
70 | wikipedia_url: string;
71 | }
72 |
73 | export class NLPSpeechAnalysisSentiment {
74 | score: number;
75 | magnitude: number;
76 | }
77 |
--------------------------------------------------------------------------------
/src/app/services/gcloud-nlp/gcloud-nlp.service.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { Injectable } from '@angular/core';
18 | import { Http, Response, Headers, RequestOptions, ResponseOptions } from '@angular/http';
19 | import { Observable } from 'rxjs/Observable';
20 | import { NLPDocument, NLPFeatures, NLPEncodingType } from '../../models/index';
21 | import { environment } from '../../../environments/environment';
22 |
23 | @Injectable()
24 | export class GCloudNLPService {
25 |
26 | constructor(public http: Http) { }
27 |
28 | /**
29 | * Ask google to do a Natural Language Processing / Analysis of the document passed
30 | * @param document Contains the text to be processed / analysed.
31 | * @param features What exactly do we want to analyse the document for (specific features? or all?)
32 | * @param encodingType The text encoding type
33 | * @returns { Observable } Response from Google Cloud
34 | */
35 | annotateText(document: NLPDocument, features: NLPFeatures, encodingType: NLPEncodingType): Observable {
36 | return this.http.post(environment.gCloudNLPApiUrl + '?key=' + environment.gCloudProjectApiKey,
37 | { document: document, features: features, encodingType: encodingType})
38 | .map((res: Response) => {
39 | if (res.status === 401) {
40 | throw Error('Not Authorized.');
41 | } else {
42 | return res.json();
43 | }
44 | });
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "google-speech-demo",
3 | "version": "0.0.1",
4 | "description": "Demo App for Google Cloud Speech, Natural Language and Vision APIs",
5 | "license": "Apache License, Version 2.0",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/gridcellcoder/google-speech-demos"
9 | },
10 | "author": "GridCell",
11 | "contributors": [
12 | "Nuno Freire =4.1.2",
39 | "core-js": "^2.4.1",
40 | "font-awesome": "^4.7.0",
41 | "hammerjs": "^2.0.8",
42 | "rxjs": "^5.4.1",
43 | "zone.js": "^0.8.12"
44 | },
45 | "devDependencies": {
46 | "@angular/cli": "1.1.2",
47 | "@angular/compiler-cli": "^4.2.3",
48 | "@angular/language-service": "^4.2.3",
49 | "@types/hammerjs": "2.0.34",
50 | "@types/jasmine": "^2.5.52",
51 | "@types/node": "~6.0.60",
52 | "codelyzer": "~3.0.1",
53 | "jasmine-core": "~2.6.4",
54 | "jasmine-spec-reporter": "~4.1.0",
55 | "karma": "~1.7.0",
56 | "karma-chrome-launcher": "~2.1.1",
57 | "karma-cli": "~1.0.1",
58 | "karma-jasmine": "~1.1.0",
59 | "karma-jasmine-html-reporter": "^0.2.2",
60 | "karma-coverage-istanbul-reporter": "^1.3.0",
61 | "protractor": "~5.1.2",
62 | "ts-node": "~3.0.6",
63 | "tslint": "~5.4.3",
64 | "typescript": "~2.3.4"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/app/services/gcloud-vision/gcloud-vision.service.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { Injectable } from '@angular/core';
18 | import { Http, Response, Headers, RequestOptions, ResponseOptions } from '@angular/http';
19 | import { Observable } from 'rxjs/Observable';
20 | import { VisionAnnotateRequest, VisionFeature } from '../../models/index';
21 | import { environment } from '../../../environments/environment';
22 |
23 |
24 | @Injectable()
25 | export class GCloudVisionService {
26 |
27 | constructor(public http: Http) { }
28 |
29 | /**
30 | * Ask google process the image passed.
31 | * @param imageBase64 Contains the image to be processed / analysed in base64 format.
32 | * @param features What exactly do we want to analyse the document for (specific features? or all?)
33 | * @returns { Observable } Response from Google Cloud
34 | */
35 | annotateImage(imageBase64: string, features: VisionFeature[]): Observable {
36 | let options = new RequestOptions({
37 | headers: new Headers({
38 | 'Content-Type': 'application/json'
39 | })
40 | });
41 |
42 | let request: VisionAnnotateRequest = {
43 | requests: [
44 | {
45 | image: {
46 | content: imageBase64
47 | },
48 | features: features
49 | }
50 | ]
51 | };
52 |
53 | return this.http.post(environment.gCloudVisionApiUrl + '?key=' + environment.gCloudProjectApiKey,
54 | request)
55 | .map((res: Response) => {
56 | if (res.status === 401) {
57 | throw Error('Not Authorized.');
58 | } else {
59 | return res.json();
60 | }
61 | });
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { BrowserModule } from '@angular/platform-browser';
18 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
19 | import { HttpModule } from '@angular/http';
20 | import { NgModule } from '@angular/core';
21 | import { FormsModule } from '@angular/forms';
22 |
23 | import { MaterialModule } from '@angular/material';
24 | import { AgmCoreModule } from '@agm/core';
25 | import { BusyModule, BusyConfig } from 'angular2-busy';
26 |
27 | import { AppComponent } from './app.component';
28 | import { AppRoutingModule } from './app-routing.module';
29 | import { DialogAlertComponent } from './components/index';
30 | import { PageWidgetsComponent } from './pages/index';
31 | import { WebSpeechService,
32 | WebSynthService,
33 | GCloudSpeechService,
34 | GCloudNLPService,
35 | GCloudVisionService } from './services/index';
36 |
37 | import { environment } from '../environments/environment';
38 |
39 |
40 | @NgModule({
41 | declarations: [
42 | AppComponent,
43 |
44 | DialogAlertComponent,
45 |
46 | PageWidgetsComponent,
47 | ],
48 | entryComponents: [ DialogAlertComponent ],
49 | imports: [
50 | AppRoutingModule,
51 |
52 | BrowserModule,
53 | BrowserAnimationsModule,
54 | FormsModule,
55 | HttpModule,
56 | MaterialModule,
57 | AgmCoreModule.forRoot({
58 | apiKey: environment.gCloudProjectApiKey
59 | }),
60 | BusyModule.forRoot({
61 | message: '',
62 | backdrop: true,
63 | template: '
',
64 | delay: 200,
65 | minDuration: 250,
66 | wrapperClass: 'ng-busy'
67 | })
68 | ],
69 | providers: [
70 | WebSpeechService,
71 | WebSynthService,
72 | GCloudNLPService,
73 | GCloudSpeechService,
74 | GCloudVisionService
75 | ],
76 | bootstrap: [ AppComponent ]
77 | })
78 | export class AppModule { }
79 |
--------------------------------------------------------------------------------
/src/app/pages/widgets/widgets.component.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
18 | import { NoopAnimationsModule } from '@angular/platform-browser/animations';
19 | import { FormsModule } from '@angular/forms';
20 | import { MaterialModule } from '@angular/material';
21 | import { AgmCoreModule } from '@agm/core';
22 | import { BusyModule, BusyConfig } from 'angular2-busy';
23 | import { PageWidgetsComponent } from './widgets.component';
24 | import { WebSpeechService,
25 | GCloudSpeechService,
26 | GCloudNLPService,
27 | GCloudVisionService } from '../../services/index';
28 |
29 | class MockWebSpeechService {
30 | start() { }
31 | stop() { }
32 | isRecognizing() { };
33 | }
34 | class MockGCloudSpeechService {
35 | start() { }
36 | stop() { }
37 | isRecognizing() { };
38 | }
39 | class MockGCloudNLPService {
40 | annotateText() { }
41 | }
42 | class MockGCloudVisionService {
43 | annotateImage() { }
44 | }
45 |
46 | describe('PageWidgetsComponent', () => {
47 | let component: PageWidgetsComponent;
48 | let fixture: ComponentFixture;
49 |
50 | beforeEach(async(() => {
51 | TestBed.configureTestingModule({
52 | declarations: [
53 | PageWidgetsComponent,
54 | ],
55 | imports: [
56 | NoopAnimationsModule,
57 | FormsModule,
58 | MaterialModule,
59 | AgmCoreModule.forRoot(),
60 | BusyModule
61 | ],
62 | providers: [
63 | {provide: WebSpeechService, useClass: MockWebSpeechService},
64 | {provide: GCloudSpeechService, useClass: MockGCloudSpeechService},
65 | {provide: GCloudNLPService, useClass: MockGCloudNLPService},
66 | {provide: GCloudVisionService, useClass: MockGCloudVisionService},
67 | ]
68 | }).compileComponents();
69 | }));
70 |
71 | beforeEach(() => {
72 | fixture = TestBed.createComponent(PageWidgetsComponent);
73 | component = fixture.componentInstance;
74 | fixture.detectChanges();
75 | });
76 |
77 | it('should create', () => {
78 | expect(component).toBeTruthy();
79 | });
80 |
81 | it('should toggle web speech', () => {
82 | let result = true;
83 | let spy1 = spyOn((component).webSpeechService, 'isRecognizing').and.callFake(() => { return result; });
84 | let spy2 = spyOn(component, 'stopWebSpeech');
85 | let spy3 = spyOn(component, 'startWebSpeech');
86 |
87 | component.toggleWebSpeech();
88 | expect(spy1).toHaveBeenCalled();
89 | expect(spy2).toHaveBeenCalled();
90 | expect(spy3).not.toHaveBeenCalled();
91 |
92 | result = false;
93 | component.toggleWebSpeech();
94 | expect(spy1).toHaveBeenCalled();
95 | expect(spy3).toHaveBeenCalled();
96 | });
97 | });
98 |
--------------------------------------------------------------------------------
/src/assets/img/loading.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * This file includes polyfills needed by Angular and is loaded before the app.
19 | * You can add your own extra polyfills to this file.
20 | *
21 | * This file is divided into 2 sections:
22 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
23 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
24 | * file.
25 | *
26 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
27 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
28 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
29 | *
30 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
31 | */
32 |
33 | /***************************************************************************************************
34 | * BROWSER POLYFILLS
35 | */
36 |
37 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
38 | // import 'core-js/es6/symbol';
39 | // import 'core-js/es6/object';
40 | // import 'core-js/es6/function';
41 | // import 'core-js/es6/parse-int';
42 | // import 'core-js/es6/parse-float';
43 | // import 'core-js/es6/number';
44 | // import 'core-js/es6/math';
45 | // import 'core-js/es6/string';
46 | // import 'core-js/es6/date';
47 | // import 'core-js/es6/array';
48 | // import 'core-js/es6/regexp';
49 | // import 'core-js/es6/map';
50 | // import 'core-js/es6/weak-map';
51 | // import 'core-js/es6/set';
52 |
53 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
54 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
55 |
56 | /** IE10 and IE11 requires the following to support `@angular/animation`. */
57 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
58 |
59 |
60 | /** Evergreen browsers require these. **/
61 | import 'core-js/es6/reflect';
62 | import 'core-js/es7/reflect';
63 |
64 |
65 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/
66 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
67 |
68 |
69 |
70 | /***************************************************************************************************
71 | * Zone JS is required by Angular itself.
72 | */
73 | import 'zone.js/dist/zone'; // Included with Angular CLI.
74 |
75 |
76 |
77 | /***************************************************************************************************
78 | * APPLICATION IMPORTS
79 | */
80 |
81 | /**
82 | * Date, currency, decimal and percent pipes.
83 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
84 | */
85 | // import 'intl'; // Run `npm install --save intl`.
86 | /**
87 | * Need to import at least one locale-data with intl.
88 | */
89 | // import 'intl/locale-data/jsonp/en';
90 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": false,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "eofline": true,
15 | "forin": true,
16 | "import-blacklist": [
17 | true,
18 | "rxjs"
19 | ],
20 | "import-spacing": true,
21 | "indent": [
22 | true,
23 | "spaces"
24 | ],
25 | "interface-over-type-literal": true,
26 | "label-position": true,
27 | "max-line-length": [
28 | true,
29 | 140
30 | ],
31 | "member-access": false,
32 | "member-ordering": [
33 | true,
34 | "static-before-instance",
35 | "variables-before-functions"
36 | ],
37 | "no-arg": true,
38 | "no-bitwise": true,
39 | "no-console": [
40 | true,
41 | "debug",
42 | "info",
43 | "time",
44 | "timeEnd",
45 | "trace"
46 | ],
47 | "no-construct": true,
48 | "no-debugger": true,
49 | "no-duplicate-super": true,
50 | "no-empty": false,
51 | "no-empty-interface": true,
52 | "no-eval": true,
53 | "no-inferrable-types": [
54 | true,
55 | "ignore-params"
56 | ],
57 | "no-misused-new": true,
58 | "no-non-null-assertion": true,
59 | "no-shadowed-variable": true,
60 | "no-string-literal": false,
61 | "no-string-throw": true,
62 | "no-switch-case-fall-through": true,
63 | "no-trailing-whitespace": true,
64 | "no-unnecessary-initializer": true,
65 | "no-unused-expression": true,
66 | "no-use-before-declare": true,
67 | "no-var-keyword": true,
68 | "object-literal-sort-keys": false,
69 | "one-line": [
70 | true,
71 | "check-open-brace",
72 | "check-catch",
73 | "check-else",
74 | "check-whitespace"
75 | ],
76 | "prefer-const": false,
77 | "quotemark": [
78 | true,
79 | "single"
80 | ],
81 | "radix": true,
82 | "semicolon": [
83 | "always"
84 | ],
85 | "triple-equals": [
86 | true,
87 | "allow-null-check"
88 | ],
89 | "typedef-whitespace": [
90 | true,
91 | {
92 | "call-signature": "nospace",
93 | "index-signature": "nospace",
94 | "parameter": "nospace",
95 | "property-declaration": "nospace",
96 | "variable-declaration": "nospace"
97 | }
98 | ],
99 | "typeof-compare": true,
100 | "unified-signatures": true,
101 | "variable-name": false,
102 | "whitespace": [
103 | true,
104 | "check-branch",
105 | "check-decl",
106 | "check-operator",
107 | "check-separator",
108 | "check-type"
109 | ],
110 | "directive-selector": [
111 | true,
112 | "attribute",
113 | "app",
114 | "camelCase"
115 | ],
116 | "component-selector": [
117 | false,
118 | "element",
119 | "app",
120 | "kebab-case"
121 | ],
122 | "use-input-property-decorator": true,
123 | "use-output-property-decorator": true,
124 | "use-host-property-decorator": true,
125 | "no-input-rename": true,
126 | "no-output-rename": true,
127 | "use-life-cycle-interface": true,
128 | "use-pipe-transform-interface": true,
129 | "component-class-suffix": true,
130 | "directive-class-suffix": true,
131 | "no-access-missing-member": true,
132 | "templates-use-public": true,
133 | "invoke-injectable": true
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Speech, NL & Vision API Demos (by GridCell)
2 |
3 | This project showcases some of the APIs made available by the Google Cloud platform, namely:
4 | - [Google Cloud Speech](https://cloud.google.com/speech/)
5 | - [Google Cloud Natural Language](https://cloud.google.com/natural-language/)
6 | - [Google Cloud Vision](https://cloud.google.com/vision/)
7 |
8 | It also showcases the Web Speech API made available by all modern browsers, as a mean of comparision against Google Cloud Speech.
9 |
10 | ## Getting started
11 | Either:
12 | - Clone the project & follow the instructions below
13 | - See it live at: (https://gcpdemo.gridcell.io/). The local server process is still required for GCP Speech Demo.
14 |
15 | The project was generated with [Angular CLI](https://github.com/angular/angular-cli) and its structure remains pretty much unchanged; Only new pages / components / services were added along with a complementary Node.js server process. [Material Design](https://material.angular.io/) and [Bootstrap](http://getbootstrap.com/) were used just to make the web app look nicer.
16 |
17 | ## Why is a server process needed?
18 |
19 | While Google Cloud provides a REST and a RPC API, usually it is the client libraries that are the more complete way of accessing its features. That said, we were able to use the REST API for both Natural Language and Vision processing, but to be able to "stream" the speech for recognizing, we had to use the client library which is only available for server side languages.
20 |
21 | Hence the reason for the "speech-server". The following flow can be observed:
22 | - the web app captures and streams the audio through a websocket (binaryJS) to a Node.js process;
23 | - in turn, the server process streams it to GCS through its client library implementation;
24 | - once a response is returned from GCS, it is piped back to the web app.
25 |
26 | ## Project structure
27 |
28 | The meaningful bits and pieces of the project can be found in the following folders:
29 | - src/app/services/
30 | - src/app/pages/widgets/
31 | - speech-server/src/
32 |
33 | The `src/app/services/` holds the services responsible for audio capture and integrating with the Google Cloud REST API or with our own Speech-Server. The services are broken down into separate files according to their responsibilities: Speech (WebSpeech & GoogleCloud), Natural Language Processing (NLP) and Vision APIs.
34 |
35 | The `src/app/pages/widgets/` contains the main web app page. It basically consists of 3 cards showcasing the aforementioned features. They could / should have been split into separate components but are all bundled together so you can easily see and compare the relevant code.
36 |
37 | The `speech-server/src` holds the Node.js typescript implementation of the server process responsible for piping the audio stream from the wep app to the Google Cloud Speech and back.
38 |
39 | ## Configuration
40 |
41 | Before being able to run both web app and speech server, we need to configure the google cloud project ID and API Key. This can be done in the following files:
42 | - src/environments/environment.ts (and / or environment.prod.ts)
43 | - speech-server/src/environment.ts
44 |
45 | ## Web app (local) server
46 |
47 | Run `npm install` and then `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
48 |
49 | ## Speech-(local)-server
50 |
51 | Since the speech-server relies on google cloud's client library, to run it on your local workstation you must first install the [Google Cloud SDK](https://cloud.google.com/sdk/docs/) and authenticate by running the following command: `gcloud auth application-default login`
52 |
53 | Then `cd speech-server` and run `npm start` on a separate terminal window. The server will be accessable on `ws://localhost:8000`.
54 |
55 | ## Build
56 |
57 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
58 |
59 | ## Running unit tests
60 |
61 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
62 | The coverage is not exhaustive in any way, just wanting to provide a few examples.
63 |
64 | ## Running end-to-end tests
65 |
66 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
67 | Before running the tests make sure you are serving the app via `ng serve`.
68 | The coverage is not exaustive in any way, just wanting to provide a few examples.
69 |
70 | ## Further help
71 |
72 | Tweet us @gridcell_io
73 |
74 | All trademarks acknowledged, this is not a Google product nor affiliated with Google, Google Cloud Services.
75 |
--------------------------------------------------------------------------------
/src/app/services/web-speech/web-speech.service.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { Injectable, NgZone, EventEmitter } from '@angular/core';
18 | import { Observable } from 'rxjs/Observable';
19 | import { Observer } from 'rxjs/Observer';
20 | import { IEvent } from '../../models/index';
21 |
22 | // Extend the Window class with the Web Speech API -> SpeechRecognition engines
23 | // for the different browser types
24 | interface IWindow extends Window {
25 | webkitSpeechRecognition: any;
26 | mozSpeechRecognition: any;
27 | msSpeechRecognition: any;
28 | SpeechRecognition: any;
29 | }
30 |
31 | @Injectable()
32 | export class WebSpeechService {
33 |
34 | private engine: any = null;
35 |
36 | private recognizing = false;
37 | private observer: Observer;
38 |
39 | constructor(private zone: NgZone) {
40 | this.create();
41 | }
42 |
43 | /**
44 | * Starts the audio capture and speech recognition engine.
45 | * @returns {Observable} Observable that emits any event related to the speech recognition,
46 | * including the resulting transcript and any error that might occur...
47 | */
48 | start(): Observable {
49 | if (!this.recognizing) {
50 | this.engine.start();
51 | }
52 | return new Observable((observer: Observer) => { this.observer = this.observer || observer; });
53 | }
54 |
55 | /**
56 | * Stops the audio capture and speech recognition engine.
57 | */
58 | stop() {
59 | this.engine.stop();
60 |
61 | if (this.observer) {
62 | // Give it some time to any additional event to propragate to subscribers...
63 | setTimeout(() => { this.observer = null; }, 500);
64 | }
65 | }
66 |
67 | /**
68 | * Returns true if audio capture is in progress; false, otherwise.
69 | * @returns {boolean}
70 | */
71 | isRecognizing(): boolean {
72 | return this.recognizing;
73 | }
74 |
75 | /**
76 | * Helper function to create SpeechRecognition engine and bind relevant events.
77 | */
78 | private create() {
79 | this.engine = this.createEngine();
80 | this.engine.continuous = true;
81 | this.engine.lang = 'en-US';
82 | // this.engine.interimResults = true;
83 | // this.engine.maxAlternatives = 1;
84 |
85 | this.engine.onerror = this.onerror.bind(this);
86 | this.engine.onresult = this.onresult.bind(this);
87 | this.engine.onaudiostart = this.onaudiostart.bind(this);
88 | this.engine.onaudioend = this.onaudioend.bind(this);
89 | this.engine.onnomatch = this.onnomatch.bind(this);
90 | }
91 |
92 | /**
93 | * Helper function to create SpeechRecognition object supporting multiple browsers' engines.
94 | */
95 | private createEngine(): any {
96 | const win: IWindow = window;
97 | return new (win.webkitSpeechRecognition ||
98 | win.mozSpeechRecognition ||
99 | win.msSpeechRecognition ||
100 | win.SpeechRecognition)();
101 | };
102 |
103 | private onaudiostart() {
104 | this.recognizing = true;
105 |
106 | this.zone.run(() => {
107 | this.observer.next({
108 | type: 'hint',
109 | value: 'Capturing audio...'
110 | });
111 | });
112 | }
113 |
114 | private onaudioend() {
115 | this.recognizing = false;
116 |
117 | this.zone.run(() => {
118 | this.observer.next({
119 | type: 'hint',
120 | value: 'Stopped capturing audio.'
121 | });
122 | });
123 | }
124 |
125 | private onnomatch() {
126 | this.zone.run(() => {
127 | this.observer.next({
128 | type: 'hint',
129 | value: 'No match!'
130 | });
131 | });
132 | }
133 |
134 | private onerror(event: any) {
135 | this.recognizing = false;
136 |
137 | this.zone.run(() => {
138 | this.observer.error({
139 | type: 'error',
140 | value: event.error
141 | });
142 | });
143 |
144 | this.stop();
145 | }
146 |
147 | private onresult(event: any) {
148 | this.zone.run(() => {
149 | this.transcriptText(event);
150 | });
151 | }
152 |
153 | /**
154 | * Basic parsing of the speech recognition result object, emitting 'tag' event for subscribers.
155 | * @param event The onresult event returned by the SpeechRecognition engine
156 | */
157 | private transcriptText(event: any) {
158 | for (let i = event.resultIndex; i < event.results.length; ++i) {
159 | if (event.results[i].isFinal) {
160 | this.observer.next({
161 | type: 'tag',
162 | value: event.results[i][0].transcript
163 | });
164 | }
165 | }
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/src/app/services/gcloud-nlp/gcloud-nlp.service.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import { inject, fakeAsync, tick, TestBed } from '@angular/core/testing';
17 | import { MockBackend, MockConnection } from '@angular/http/testing';
18 | import {
19 | Http,
20 | BaseRequestOptions,
21 | Response,
22 | ResponseOptions, RequestMethod, Headers
23 | } from '@angular/http';
24 |
25 | import { GCloudNLPService } from '../index';
26 | import {
27 | NLPDocument,
28 | NLPDocumentType,
29 | NLPEncodingType,
30 | NLPFeatures,
31 | NLPSpeechAnalysis
32 | } from '../../models/index';
33 | import { environment } from '../../../environments/environment';
34 |
35 | describe('GCloudNLPService', () => {
36 | let document: NLPDocument = {
37 | type: NLPDocumentType.PLAIN_TEXT,
38 | language: 'en-US',
39 | content: 'text'
40 | };
41 | let features: NLPFeatures = {
42 | extractSyntax: true,
43 | extractEntities: true,
44 | extractDocumentSentiment: true
45 | }
46 | let fakeTestObject: any = { dummy: 1 };
47 |
48 | beforeEach(() => {
49 | TestBed.configureTestingModule({
50 | providers: [
51 | GCloudNLPService,
52 | BaseRequestOptions,
53 | MockBackend,
54 | {
55 | provide: Http,
56 | useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
57 | return new Http(backend, defaultOptions);
58 | }, deps: [MockBackend, BaseRequestOptions]
59 | },
60 | ]
61 | });
62 | });
63 |
64 | function prepareMockBackendToReturnResponseAndExpectHTTPStuff(backend: MockBackend, method: RequestMethod, url: string, body?: any) {
65 | backend.connections.subscribe((connection: MockConnection) => {
66 | expect(connection.request.method).toBe(method);
67 | expect(connection.request.url).toBe(url + '?key=' + environment.gCloudProjectApiKey);
68 | if (body) {
69 | expect(connection.request.getBody()).toBe(JSON.stringify(body, null, 2));
70 | }
71 |
72 | let response = new ResponseOptions({ body: JSON.stringify(fakeTestObject) });
73 | connection.mockRespond(new Response(response));
74 | });
75 | }
76 |
77 | function prepareMockBackendToFailAuthentication(backend: MockBackend) {
78 | backend.connections.subscribe((connection: MockConnection) => {
79 | let response = new ResponseOptions({ status: 401, statusText: 'Not Authorized' });
80 | connection.mockRespond(new Response(response));
81 | });
82 | }
83 |
84 | function prepareMockBackendToReturnError(backend: MockBackend) {
85 | backend.connections.subscribe((connection: MockConnection) => {
86 | let response = new ResponseOptions({ body: JSON.stringify(fakeTestObject) });
87 | connection.mockError(new Error('Error.'));
88 | });
89 | }
90 |
91 |
92 | describe('annotateText', () => {
93 | it('should query the REST endpoint successfully',
94 | inject([GCloudNLPService, MockBackend], fakeAsync((service: GCloudNLPService, backend: MockBackend) => {
95 | prepareMockBackendToReturnResponseAndExpectHTTPStuff(backend,
96 | RequestMethod.Post,
97 | environment.gCloudNLPApiUrl,
98 | {document: document, features: features, encodingType: NLPEncodingType.UTF16});
99 |
100 | service.annotateText(document, features, NLPEncodingType.UTF16).subscribe((res) => {
101 | expect(res).toBeDefined();
102 | });
103 | tick();
104 | }))
105 | );
106 |
107 | it('should throw exception if invalid API KEY is passed or no longer valid',
108 | inject([GCloudNLPService, MockBackend], fakeAsync((service: GCloudNLPService, backend: MockBackend) => {
109 | prepareMockBackendToFailAuthentication(backend);
110 |
111 | service.annotateText(document, features, NLPEncodingType.UTF16).subscribe((res) => {
112 | expect(res.status).toBe(401);
113 | }, (error) => {
114 | expect(error.message).toBe('Not Authorized.');
115 | });
116 | tick();
117 | }))
118 | );
119 |
120 | it('should catch server error after enough time for retries',
121 | inject([GCloudNLPService, MockBackend], fakeAsync((service: GCloudNLPService, backend: MockBackend) => {
122 | prepareMockBackendToReturnError(backend);
123 |
124 | let errorCaught = false;
125 | service.annotateText(document, features, NLPEncodingType.UTF16).subscribe((res) => {
126 | expect(1).toBe(2); // this should never happen
127 | }, (error) => {
128 | expect(error.message).toBe('Error.');
129 | errorCaught = true;
130 | });
131 |
132 | expect(errorCaught).toBeTruthy();
133 | }))
134 | );
135 | });
136 |
137 | });
138 |
--------------------------------------------------------------------------------
/src/app/services/gcloud-vision/gcloud-vision.service.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. GridCell Ltd
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { inject, fakeAsync, tick, TestBed } from '@angular/core/testing';
18 | import { MockBackend, MockConnection } from '@angular/http/testing';
19 | import {
20 | Http,
21 | BaseRequestOptions,
22 | Response,
23 | ResponseOptions, RequestMethod, Headers
24 | } from '@angular/http';
25 |
26 | import { GCloudVisionService } from '../index';
27 | import {
28 | VisionAnnotateRequest,
29 | VisionFeatureType,
30 | VisionFeature
31 | } from '../../models/index';
32 | import { environment } from '../../../environments/environment';
33 |
34 | describe('GCloudVisionService', () => {
35 | let features: VisionFeature[] = [
36 | { type: VisionFeatureType.WEB_DETECTION, maxResults: 3 }
37 | ];
38 | let fakeTestObject: any = { dummy: 1 };
39 |
40 | beforeEach(() => {
41 | TestBed.configureTestingModule({
42 | providers: [
43 | GCloudVisionService,
44 | BaseRequestOptions,
45 | MockBackend,
46 | {
47 | provide: Http,
48 | useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
49 | return new Http(backend, defaultOptions);
50 | }, deps: [MockBackend, BaseRequestOptions]
51 | },
52 | ]
53 | });
54 | });
55 |
56 | function prepareMockBackendToReturnResponseAndExpectHTTPStuff(backend: MockBackend, method: RequestMethod, url: string, body?: any) {
57 | backend.connections.subscribe((connection: MockConnection) => {
58 | expect(connection.request.method).toBe(method);
59 | expect(connection.request.url).toBe(url + '?key=' + environment.gCloudProjectApiKey);
60 | if (body) {
61 | expect(connection.request.getBody()).toBe(JSON.stringify(body, null, 2));
62 | }
63 |
64 | let response = new ResponseOptions({ body: JSON.stringify(fakeTestObject) });
65 | connection.mockRespond(new Response(response));
66 | });
67 | }
68 |
69 | function prepareMockBackendToFailAuthentication(backend: MockBackend) {
70 | backend.connections.subscribe((connection: MockConnection) => {
71 | let response = new ResponseOptions({ status: 401, statusText: 'Not Authorized' });
72 | connection.mockRespond(new Response(response));
73 | });
74 | }
75 |
76 | function prepareMockBackendToReturnError(backend: MockBackend) {
77 | backend.connections.subscribe((connection: MockConnection) => {
78 | let response = new ResponseOptions({ body: JSON.stringify(fakeTestObject) });
79 | connection.mockError(new Error('Error.'));
80 | });
81 | }
82 |
83 |
84 | describe('annotateText', () => {
85 | it('should query the REST endpoint successfully',
86 | inject([GCloudVisionService, MockBackend], fakeAsync((service: GCloudVisionService, backend: MockBackend) => {
87 | let request: VisionAnnotateRequest = {
88 | requests: [
89 | {
90 | image: {
91 | content: 'imageBase64'
92 | },
93 | features: features
94 | }
95 | ]
96 | };
97 |
98 | prepareMockBackendToReturnResponseAndExpectHTTPStuff(backend,
99 | RequestMethod.Post,
100 | environment.gCloudVisionApiUrl,
101 | request);
102 |
103 | service.annotateImage('imageBase64', features).subscribe((res) => {
104 | expect(res).toBeDefined();
105 | });
106 | tick();
107 | }))
108 | );
109 |
110 | it('should throw exception if invalid API KEY is passed or no longer valid',
111 | inject([GCloudVisionService, MockBackend], fakeAsync((service: GCloudVisionService, backend: MockBackend) => {
112 | prepareMockBackendToFailAuthentication(backend);
113 |
114 | service.annotateImage('imageBase64', features).subscribe((res) => {
115 | expect(res.status).toBe(401);
116 | }, (error) => {
117 | expect(error.message).toBe('Not Authorized.');
118 | });
119 | tick();
120 | }))
121 | );
122 |
123 | it('should catch server error after enough time for retries',
124 | inject([GCloudVisionService, MockBackend], fakeAsync((service: GCloudVisionService, backend: MockBackend) => {
125 | prepareMockBackendToReturnError(backend);
126 |
127 | let errorCaught = false;
128 | service.annotateImage('imageBase64', features).subscribe((res) => {
129 | expect(1).toBe(2); // this should never happen
130 | }, (error) => {
131 | expect(error.message).toBe('Error.');
132 | errorCaught = true;
133 | });
134 |
135 | expect(errorCaught).toBeTruthy();
136 | }))
137 | );
138 | });
139 |
140 | });
141 |
--------------------------------------------------------------------------------
/src/app/services/web-synth/web-synth.service.ts:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright 2017 GridCell Ltd
3 |
4 | Licensed to the Apache Software Foundation (ASF) under one
5 | or more contributor license agreements. See the NOTICE file
6 | distributed with this work for additional information
7 | regarding copyright ownership. The ASF licenses this file
8 | to you under the Apache License, Version 2.0 (the
9 | "License"); you may not use this file except in compliance
10 | with the License. You may obtain a copy of the License at
11 |
12 | http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Unless required by applicable law or agreed to in writing,
15 | software distributed under the License is distributed on an
16 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | KIND, either express or implied. See the License for the
18 | specific language governing permissions and limitations
19 | under the License.
20 | */
21 |
22 | import { Injectable, NgZone, EventEmitter } from '@angular/core';
23 | import { Observable } from 'rxjs/Observable';
24 | import { Observer } from 'rxjs/Observer';
25 | import { BinaryClient } from 'binaryjs-client';
26 | import { IEvent } from '../../models/index';
27 | import { environment } from '../../../environments/environment';
28 |
29 | // Extend the Window class with the Web Audio API -> SpeechSynthesis engine
30 | // for the different browser types
31 | interface IWindow extends Window {
32 | webkitSpeechSynthesis: any;
33 | mozSpeechSynthesis: any;
34 | msSpeechSynthesis: any;
35 | speechSynthesis: any;
36 | }
37 |
38 | @Injectable()
39 | export class WebSynthService {
40 |
41 | private engine: any = null;
42 | private utterThis: SpeechSynthesisUtterance;
43 |
44 | private speechServerClient: any = null;
45 | private speechServerStream: any = null;
46 |
47 | private busy = false;
48 | private observer: Observer;
49 |
50 | constructor(private zone: NgZone) {
51 | this.create();
52 | }
53 |
54 | /**
55 | * Starts the speech synthetiser engine. First asks server for random sentence and speaks it out loud.
56 | * @returns {Observable} Observable that emits any event related to the speech synth,
57 | * including the utterance to be spoken and any error that might occur...
58 | */
59 | start(): Observable {
60 | if (!this.isSpeaking()) {
61 | this.getUtteranceAndSpeak();
62 | }
63 | return new Observable((observer: Observer) => { this.observer = this.observer || observer; });
64 | }
65 |
66 | /**
67 | * Stops the speech synthetiser engine removing any utterances from the queue.
68 | */
69 | stop() {
70 | this.engine.cancel();
71 |
72 | if (this.speechServerClient) {
73 | this.speechServerClient.close();
74 | this.speechServerClient = null;
75 | this.speechServerStream = null;
76 | }
77 |
78 | if (this.observer) {
79 | // Give it some time to any additional event to propragate to subscribers...
80 | setTimeout(() => { this.observer = null; }, 100);
81 | }
82 | }
83 |
84 | /**
85 | * Returns true if a utterance is being spoken; false, otherwise.
86 | * @returns {boolean}
87 | */
88 | isSpeaking(): boolean {
89 | return this.busy || this.engine.speaking;
90 | }
91 |
92 | /**
93 | * Helper function to create SpeechSynthetiser engine and bind relevant events.
94 | */
95 | private create() {
96 | this.engine = this.createSynth();
97 | this.utterThis = new SpeechSynthesisUtterance();
98 |
99 | this.utterThis.onstart = this.onstart.bind(this);
100 | this.utterThis.onend = this.onend.bind(this);
101 | this.utterThis.onerror = this.onerror.bind(this);
102 | }
103 |
104 | private getUtteranceAndSpeak() {
105 | // connect to the speech server and get stream ready for send / receive data...
106 | this.speechServerClient = new BinaryClient(environment.speechServerUrl)
107 | .on('error', this.onerror.bind(this))
108 | .on('open', () => {
109 | // special initial message to request random utterance.
110 | this.speechServerStream = this.speechServerClient.createStream({ type: 'random_utterance' });
111 | })
112 | .on('stream', (serverStream) => {
113 | serverStream
114 | .on('data', this.onutterance.bind(this))
115 | .on('error', this.onerror.bind(this))
116 | .on('close', this.onerror.bind(this))
117 | });
118 | }
119 |
120 | /**
121 | * Helper function to create SpeechSynthetiser object supporting multiple browsers' engines.
122 | */
123 | private createSynth(): SpeechSynthesis {
124 | const win: IWindow = window;
125 | return (win.webkitSpeechSynthesis ||
126 | win.mozSpeechSynthesis ||
127 | win.msSpeechSynthesis ||
128 | win.speechSynthesis);
129 | };
130 |
131 | private onstart() {
132 | this.busy = false;
133 |
134 | this.zone.run(() => {
135 | this.observer.next({
136 | type: 'start',
137 | value: 'Speaking utterance...'
138 | });
139 | });
140 | }
141 |
142 | private onend() {
143 | this.zone.run(() => {
144 | this.observer.next({
145 | type: 'end',
146 | value: 'Finished speaking.'
147 | });
148 | });
149 | }
150 |
151 | private onerror(event: any) {
152 | this.zone.run(() => {
153 | this.observer.error({
154 | type: 'error',
155 | value: 'Failed to get utterance from speech server.'
156 | });
157 | });
158 |
159 | this.stop();
160 | }
161 |
162 | private onutterance(utterance: any) {
163 | Object.assign(this.utterThis, utterance);
164 | let voice = (this.engine).getVoices().find((v: SpeechSynthesisVoice) => {
165 | return v.name === utterance.voiceName;
166 | });
167 | this.utterThis.voice = voice;
168 |
169 | this.engine.speak(this.utterThis);
170 |
171 | this.zone.run(() => {
172 | this.observer.next({
173 | type: 'tag',
174 | value: utterance.text
175 | });
176 | });
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/speech-server/src/server.ts:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright 2017 GridCell Ltd
3 |
4 | Licensed to the Apache Software Foundation (ASF) under one
5 | or more contributor license agreements. See the NOTICE file
6 | distributed with this work for additional information
7 | regarding copyright ownership. The ASF licenses this file
8 | to you under the Apache License, Version 2.0 (the
9 | "License"); you may not use this file except in compliance
10 | with the License. You may obtain a copy of the License at
11 |
12 | http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Unless required by applicable law or agreed to in writing,
15 | software distributed under the License is distributed on an
16 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | KIND, either express or implied. See the License for the
18 | specific language governing permissions and limitations
19 | under the License.
20 | */
21 |
22 | import { environment } from './environment';
23 | import * as Speech from '@google-cloud/speech';
24 | import * as binaryjs from 'binaryjs';
25 |
26 | // Instantiates the speech client and binary websocket server
27 | const speechClient = Speech({
28 | projectId: environment.gCloudProjectId
29 | });
30 | const port = Number(process.env.PORT || 8000);
31 | const server = new binaryjs.BinaryServer({
32 | port: port
33 | });
34 |
35 | // The audio file's encoding, sample rate in hertz, and BCP-47 language code
36 | let options = {
37 | config: {
38 | encoding: 'LINEAR16',
39 | languageCode: 'en-US',
40 | sampleRateHertz: 16000
41 | },
42 | singleUtterance: false,
43 | interimResults: false,
44 | verbose: true
45 | };
46 |
47 | // handle client connections
48 | server
49 | .on('error', (error) => { console.log('Server error:' + error); })
50 | .on('close', () => { console.log('Server closed'); })
51 | .on('connection', (client) => {
52 | client
53 | .on('error', (error) => { console.log('Client error: ' + error); })
54 | .on('close', () => { console.log('Client closed.'); })
55 | .on('stream', (clientStream, meta) => {
56 | console.log('New Client: ' + JSON.stringify(meta));
57 |
58 | if (meta.type === 'speech') {
59 | handleSpeechRequest(client, clientStream, meta);
60 | } else {
61 | handleRandomUtteranceRequest(client);
62 | }
63 | });
64 | });
65 |
66 | function handleSpeechRequest(client, clientStream, meta) {
67 | options.config.sampleRateHertz = meta.sampleRate;
68 |
69 | let speechStream = speechClient.createRecognizeStream(options)
70 | .on('error', (data) => { handleGCSMessage(data, client, speechStream); })
71 | .on('data', (data) => { handleGCSMessage(data, client, speechStream); })
72 | .on('close', () => { client.close(); });
73 |
74 | clientStream.pipe(speechStream);
75 | }
76 |
77 | function handleRandomUtteranceRequest(client) {
78 | let data = getRandomSentence();
79 | console.log(data);
80 |
81 | try {
82 | client.send(data);
83 | } catch (ex) {
84 | console.log('Failed to send message back to client...Closed?');
85 | }
86 | }
87 |
88 | function handleGCSMessage(data, client, speechStream) {
89 | if (client && client.streams[0] &&
90 | client.streams[0].writable && !client.streams[0].destroyed) {
91 | try {
92 | console.log(data);
93 |
94 | client.send(data);
95 | } catch (ex) {
96 | console.log('Failed to send message back to client...Closed?');
97 | }
98 | if (data.error || data.Error) {
99 | try {
100 | speechStream.end();
101 | speechStream = null;
102 | client.close();
103 | client = null;
104 | } catch (ex) {
105 | console.log('ERROR closing the streams after error!');
106 | }
107 | }
108 | }
109 | }
110 |
111 | function getRandomSentence(): any {
112 | let random = Math.round(Math.random() * 10);
113 | switch (random) {
114 | case 0:
115 | return {
116 | text: 'The old apple revels in its authority.',
117 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Male'
118 | };
119 | case 1:
120 | return {
121 | text: 'Don\'t step on the broken glass.',
122 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Male'
123 | };
124 | case 2:
125 | return {
126 | text: 'I will never be this young again. Ever. Oh damn… I just got older.',
127 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Male'
128 | };
129 | case 3:
130 | return {
131 | text: 'There was no ice cream in the freezer, nor did they have money to go to the store.',
132 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Male'
133 | };
134 | case 4:
135 | return {
136 | text: 'I think I will buy the red car, or I will lease the blue one.',
137 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Male'
138 | };
139 | case 5:
140 | return {
141 | text: 'He didn’t want to go to the dentist, yet he went anyway.',
142 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Male'
143 | };
144 | case 6:
145 | return {
146 | text: 'We have never been to Asia, nor have we visited Africa.',
147 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Female'
148 | };
149 | case 7:
150 | return {
151 | text: 'How was the math test?',
152 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Female'
153 | };
154 | case 8:
155 | return {
156 | text: 'Tom got a small piece of pie.',
157 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Female'
158 | };
159 | case 9:
160 | return {
161 | text: 'The book is in front of the table.',
162 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Female'
163 | };
164 | case 10:
165 | return {
166 | text: 'Check back tomorrow; I will see if the book has arrived.',
167 | lang: 'en-US', pitch: 1, rate: 1, volume: 1, voiceName: 'Google UK English Female'
168 | };
169 | }
170 | }
171 |
172 |
--------------------------------------------------------------------------------
/src/app/services/gcloud-speech/gcloud-speech.service.ts:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright 2017 GridCell Ltd
3 |
4 | Licensed to the Apache Software Foundation (ASF) under one
5 | or more contributor license agreements. See the NOTICE file
6 | distributed with this work for additional information
7 | regarding copyright ownership. The ASF licenses this file
8 | to you under the Apache License, Version 2.0 (the
9 | "License"); you may not use this file except in compliance
10 | with the License. You may obtain a copy of the License at
11 |
12 | http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Unless required by applicable law or agreed to in writing,
15 | software distributed under the License is distributed on an
16 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | KIND, either express or implied. See the License for the
18 | specific language governing permissions and limitations
19 | under the License.
20 | */
21 |
22 | import { Injectable, NgZone } from '@angular/core';
23 | import { Observable } from 'rxjs/Observable';
24 | import { Observer } from 'rxjs/Observer';
25 | import { BinaryClient } from 'binaryjs-client';
26 | import { IEvent } from '../../models/index';
27 | import { environment } from '../../../environments/environment';
28 |
29 | // Extend the Window class with the Web Audio API -> AudioContext
30 | // for the different browser types
31 | interface IWindow extends Window {
32 | webkitAudioContext: any;
33 | mozAudioContext: any;
34 | AudioContext: any;
35 | }
36 |
37 | // Extend the Navigator class with the MediaStream API -> GetUserMedia
38 | // for the different browser types
39 | interface INavigator extends Navigator {
40 | webkitGetUserMedia: any;
41 | mozGetUserMedia: any;
42 | GetUserMedia: any;
43 | }
44 |
45 | @Injectable()
46 | export class GCloudSpeechService {
47 |
48 | private audioContext: AudioContext = null;
49 | private scriptProcessor: ScriptProcessorNode = null;
50 | private audioInput: MediaStreamAudioSourceNode = null;
51 |
52 | private userMediaStream: MediaStream = null;
53 |
54 | private speechServerClient: any = null;
55 | private speechServerStream: any = null;
56 |
57 | private recognizing = false;
58 | private observer: Observer;
59 |
60 | constructor(private zone: NgZone) {
61 | this.audioContext = this.createAudioContext();
62 | this.scriptProcessor = this.audioContext.createScriptProcessor(2048, 1, 1);
63 | }
64 |
65 | /**
66 | * Starts the audio capture and sets up a streaming connection to the speech-server, sending the audio
67 | * for speech recognition on Google Cloud platform.
68 | * @returns {Observable} Observable that emits any event related to the speech recognition,
69 | * including the resulting transcript and any error that might occur...
70 | */
71 | start(): Observable {
72 | if (!this.recognizing) {
73 | this.getUserMedia(this.onaudiostart.bind(this), this.onerror.bind(this));
74 | this.recognizing = true;
75 | }
76 |
77 | return new Observable((observer: Observer) => { this.observer = this.observer || observer; });
78 | }
79 |
80 | /**
81 | * Stops the audio capture and speech recognition engine.
82 | */
83 | stop() {
84 | // dispose of audio inputs / streams and speech server client
85 | if (this.audioInput) {
86 | this.audioInput.disconnect();
87 | }
88 |
89 | if (this.userMediaStream) {
90 | this.userMediaStream.getTracks().map((track: any) => {
91 | track.stop();
92 | });
93 | }
94 |
95 | if (this.speechServerClient) {
96 | this.speechServerClient.close();
97 | this.speechServerClient = null;
98 | this.speechServerStream = null;
99 | }
100 |
101 | if (this.observer) {
102 | // Give it some time to any additional event to propragate to subscribers...
103 | setTimeout(() => { this.observer = null; }, 500);
104 | }
105 |
106 | this.recognizing = false;
107 | }
108 |
109 | /**
110 | * Returns true if audio capture is in progress; false, otherwise.
111 | * @returns {boolean}
112 | */
113 | isRecognizing(): boolean {
114 | return this.recognizing;
115 | }
116 |
117 | /**
118 | * Helper function to create AudioContext object supporting multiple browsers' engines.
119 | */
120 | private createAudioContext(): any {
121 | let win: IWindow = window;
122 | return new (win.AudioContext ||
123 | win.mozAudioContext ||
124 | win.webkitAudioContext)();
125 | };
126 |
127 | /**
128 | * Helper function to create GetUserMedia object supporting multiple browsers' engines.
129 | */
130 | private getUserMedia(successFn: Function, errorFn: Function): any {
131 | let nav: INavigator = navigator;
132 | let getUserMedia = nav.GetUserMedia || nav.mozGetUserMedia || nav.webkitGetUserMedia;
133 | return getUserMedia.call(navigator,
134 | { audio: true },
135 | successFn,
136 | errorFn
137 | );
138 | };
139 |
140 | /**
141 | * Event triggered when the audio capture is under way.
142 | * This is where the connection to the speech server is established and the
143 | * input audio stream is piped to the server
144 | */
145 | private onaudiostart(stream: any) {
146 | this.userMediaStream = stream;
147 |
148 | // get all the audio capture, processing and streaming ready to go...
149 | this.userMediaStream.getTracks().forEach((track: any) => {
150 | track.onended = this.onaudioend.bind(this);
151 | }
152 | );
153 |
154 | this.audioInput = this.audioContext.createMediaStreamSource(this.userMediaStream);
155 | this.audioInput.connect(this.scriptProcessor);
156 | this.scriptProcessor.connect(this.audioContext.destination)
157 | this.scriptProcessor.onaudioprocess = (event: any) => {
158 | // we're only using one audio channel here...
159 | let leftChannel = event.inputBuffer.getChannelData(0);
160 |
161 | if (this.speechServerStream) {
162 | this.speechServerStream.write(this.convertFloat32ToInt16(leftChannel));
163 | }
164 | }
165 |
166 | // connect to the speech server and get stream ready for send / receive data...
167 | this.speechServerClient = new BinaryClient(environment.speechServerUrl)
168 | .on('error', this.onerror.bind(this))
169 | .on('open', () => {
170 | // pass the sampleRate as a parameter to the server and get a reference to the communication stream.
171 | this.speechServerStream = this.speechServerClient.createStream({
172 | type: 'speech',
173 | sampleRate: this.audioContext.sampleRate
174 | });
175 | })
176 | .on('stream', (serverStream) => {
177 | serverStream
178 | .on('data', this.onresult.bind(this))
179 | .on('error', this.onerror.bind(this))
180 | .on('close', this.onerror.bind(this))
181 | });
182 |
183 | // let the subscribers know we're ready!
184 | this.zone.run(() => {
185 | this.observer.next({
186 | type: 'hint',
187 | value: 'Capturing audio...'
188 | });
189 | });
190 | }
191 |
192 | private onaudioend() {
193 | this.zone.run(() => {
194 | this.observer.next({
195 | type: 'hint',
196 | value: 'Stopped capturing audio.'
197 | });
198 | });
199 | }
200 |
201 | private onerror(error: any) {
202 | this.zone.run(() => {
203 | this.observer.error({
204 | type: 'error',
205 | value: typeof(error) === 'string' ? error : 'Couldn\'t connect to speech server.'
206 | });
207 | });
208 |
209 | this.stop();
210 | }
211 |
212 | private onresult(event: any) {
213 | if (event.error && event.error.message) {
214 | this.onerror(event.error.message);
215 | } else {
216 | this.zone.run(() => {
217 | this.transcriptText(event);
218 | });
219 | }
220 | }
221 |
222 | /**
223 | * Basic parsing of the speech recognition result object, emitting 'tag' event for subscribers.
224 | * @param event The onresult event returned by the SpeechRecognition engine
225 | */
226 | private transcriptText(event: any) {
227 | for (let i = 0; i < event.results.length; ++i) {
228 | if (event.results[i].isFinal) {
229 | this.observer.next({
230 | type: 'tag',
231 | value: event.results[i].transcript
232 | });
233 | }
234 | }
235 | }
236 |
237 | private convertFloat32ToInt16 (buffer) {
238 | let l = buffer.length;
239 | let buf = new Int16Array(l);
240 | while (l >= 0) {
241 | buf[l] = Math.min(1, buffer[l]) * 0x7FFF;
242 | l = l - 1;
243 | }
244 | return buf.buffer;
245 | }
246 |
247 | }
248 |
--------------------------------------------------------------------------------
/src/app/pages/widgets/widgets.component.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
Cloud Speech Demos by GridCell
21 |
22 | This project showcases some of the APIs made available by the Google Cloud platform, namely:
23 |
To get this to work you need to have a local instance of the speech-server up and running (See README) for full details. Then press the start button and a random sentence will be spoken..
75 |
76 |
77 |
78 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
GCloud Speech API
89 |
To get this to work you need to have a
90 | Google Compute account, install the gcloud sdk,
91 | create a google compute project. Then clone
92 | and change environment.ts with your project-id.
93 | and start the speech-server (See README) for full details. Then press the start button and say something..