Speech Recognition with Angular
Tags: 100daysofcode, challenge, webdev, angular
Published: 2023-02-08 17:39:22

On day 28, we'll work on adding keyword spotting with [Porcupine Angular SDK](Picovoice Porcupine Wake Word Engine Angular SDK.). [Keyword spotting](https://picovoice.ai/blog/keyword-spotting-voice-recognition/) is the first defined problem in speech recognition. It enables applications such as wake word detection.

**1. Create a new Angular project:**

```
ng new porcupine-angular
```
**2. Install the dependencies:**

```
npm install \
@picovoice/porcupine-angular @picovoice/web-voice-processor
```
3. Download the [Porcupine model](https://github.com/Picovoice/porcupine/raw/master/lib/common/porcupine_params.pv) and turn the binary model into a base64 string. 

```
npx pvbase64 \
-i ${DOWNLOADED_MODEL_PATH} \
-o src/lib/porcupine_params.js
```
Do not forget to replace ${DOWNLOADED_MODEL_PATH} (it's a unique path and depends on where you save the model such as  ~/Downloads/porcupine_params.pv).

4. Run the local server to load the page:

```
ng serve
```
5. Train Wake Word Models
Sign up for [Picovoice Console](https://console.picovoice.ai/) for free and go to the Porcupine Page. Type in `Hey Jarvis` (or another phrase that you want to build the model for.), select `Web (WASM)` as the platform and click on Download. Once you unzip the downloaded file, you see a file with the suffix `.ppn`. That's the `model`. 

6. Transform the `.ppn model` into a base64 string.

```
npx pvbase64 \
-i ${DOWNLOADED_PPN_PATH} \
-o src/lib/hey_jarvis.js \
-n heyJarvisKeywordModel
```
Again, do not forget to replace ${DOWNLOADED_PPN_PATH} (e.g. ~/Downloads/Hey-Jarvis_en_wasm_v2_1_0/Hey-Jarvis_en_wasm_v2_1_0.ppn)
7. Go to Picovoice Console's dashboard. Copy your `AccessKey`.
8. Create `voice_widget.component.ts` and paste the below. 

```
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

import { PorcupineService } from '@picovoice/porcupine-angular';

// @ts-ignore
import { modelParams } from '../lib/porcupine_params';
// @ts-ignore
import heyJarvisKeywordModel from '../lib/hey_jarvis';

@Component({
  selector: 'app-voice-widget',
  templateUrl: './voice_widget.component.html',
  styleUrls: ['./voice_widget.component.scss']
})
export class VoiceWidgetComponent implements OnDestroy {
  private keywordSubscription: Subscription;

  detections: Array<string> = [];

  constructor(private porcupineService: PorcupineService) {
    this.keywordSubscription = 
        porcupineService.keywordDetection$.subscribe(
      porcupineSubscription => {
        this.detections = 
            [...this.detections, porcupineSubscription.label];
      });
  }

  ngOnDestroy(): void {
    this.keywordSubscription.unsubscribe();
    this.porcupineService.release();
  }
  
  public async initEngine(): Promise<void> {
    await this.porcupineService.release();
    try {
      await this.porcupineService.init(
        ${ACCESS_KEY},
        {base64: heyJarvisKeywordModel, label: "Hey Jarvis"},
        { base64: modelParams }
      );
      await this.porcupineService.start();
    }
    catch (error: any) {
      
    }
  }
}
Create voice_widget.component.html and paste the following into it.
<div class="voice-widget">
    <h1>VoiceWidget</h1>
    <h3>
      <button class="start-button" (click)="initEngine()">
        Start
      </button>
    </h3>
  
    <ul>
      <li *ngFor="let keywordLabel of detections">
        "{{ keywordLabel }}"
      </li>
    </ul>
</div>
```
Update `app.component.ts`

```
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'porcupine-angular-demo-porcupine';
  show = true;

  toggle(): void {
    this.show = !this.show;
  }
}
```
Update `app.module.ts` 

```
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { VoiceWidgetComponent } from "./voice_widget.component"

@NgModule({
  declarations: [
    AppComponent,
    VoiceWidgetComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

If it doesn't work, make sure that you replaced the ${ACCESS_KEY} with your AccessKey obtained from Picovoice Console. You can also check the [original article](https://picovoice.ai/blog/add-a-wake-word-to-angular-apps-in-10-mins/) or [open-source repo](https://github.com/Picovoice/porcupine/tree/master/demo/angular) and create an issue under the repo to get technical support.