Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
panaC authored Nov 6, 2024
2 parents a804281 + 55e98e6 commit c7630f5
Show file tree
Hide file tree
Showing 34 changed files with 7,413 additions and 2 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Node.js CI

on:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Install dependencies
run: npm ci
- name: Run Build
run: npm run build
- name: Run tests
run: npm test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

node_modules/
script/web-speech-recommended-voices/
.DS_Store
114 changes: 112 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ For our initial work on this project, we're focusing on voice selection based on

The outline of this work has been explored in a [GitHub discussion](https://github.com/HadrienGardeur/web-speech-recommended-voices/discussions/9) and through a [best practices document](https://github.com/HadrienGardeur/read-aloud-best-practices/blob/main/voice-selection.md).

It's currently [under review in a draft PR](https://github.com/readium/speech/pull/7).

It's currently [under review in a draft PR](https://github.com/readium/speech/pull/7).

## Demo

Expand All @@ -33,4 +33,114 @@ It demonstrates the following features:
- fetching a list of all available languages, translating them to the user's locale and sorting them based on these translations
- returning a list of voices for a given language, grouped by region and sorted based on quality
- filtering languages and voices based on gender and offline availability
- using embedded test utterances to demo voices
- using embedded test utterances to demo voices
=======

## QuickStart

`npm install readium-speech`

#### node :
```
import { getVoices } from "readium-speech/build/cjs/voices";
const voices = await getVoices();
console.log(voices);
```

#### web :
```
import { getVoices } from "readium-speech/build/mjs/voices";
const voices = await getVoices();
console.log(voices);
```

## API

### Interface

```
export interface IVoices {
label: string;
voiceURI: string;
name: string;
language: string;
gender?: TGender | undefined;
age?: string | undefined;
offlineAvailability: boolean;
quality?: TQuality | undefined;
pitchControl: boolean;
recommendedPitch?: number | undefined;
recommendedRate?: number | undefined;
}
export interface ILanguages {
label: string;
code: string;
count: number;
}
```


#### Parse and Extract IVoices from speechSynthesis WebAPI
```
function getVoices(preferredLanguage?: string[] | string, localization?: string): Promise<IVoices[]>
```

#### List languages from IVoices
```
function getLanguages(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): ILanguages[]
```

#### helpers

```
function listLanguages(voices: IVoices[], localization?: string): ILanguages[]
function ListRegions(voices: IVoices[], localization?: string): ILanguages[]
function parseSpeechSynthesisVoices(speechSynthesisVoices: SpeechSynthesisVoice[]): IVoices[]
function getSpeechSynthesisVoices(): Promise<SpeechSynthesisVoice[]>
```

#### groupBy

```
function groupByKindOfVoices(allVoices: IVoices[]): TGroupVoices
function groupByRegions(voices: IVoices[], language: string, preferredRegions?: string[] | string, localization?: string): TGroupVoices
function groupByLanguage(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string): TGroupVoices
```

#### sortBy

```
function sortByLanguage(voices: IVoices[], preferredLanguage?: string[] | string): IVoices[]
function sortByRegion(voices: IVoices[], preferredRegions?: string[] | string, localization?: string | undefined): IVoices[]
function sortByGender(voices: IVoices[], genderFirst: TGender): IVoices[]
function sortByName(voices: IVoices[]): IVoices[]
function sortByQuality(voices: IVoices[]): IVoices[]
```

#### filterOn

```
function filterOnRecommended(voices: IVoices[], _recommended?: IRecommended[]): TReturnFilterOnRecommended
function filterOnVeryLowQuality(voices: IVoices[]): IVoices[]
function filterOnNovelty(voices: IVoices[]): IVoices[]
function filterOnQuality(voices: IVoices[], quality: TQuality | TQuality[]): IVoices[]
function filterOnLanguage(voices: IVoices[], language: string | string[]): IVoices[]
function filterOnGender(voices: IVoices[], gender: TGender): IVoices[]
```
148 changes: 148 additions & 0 deletions build/cjs/data.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
export declare const novelty: string[];
export declare const veryLowQuality: string[];
export type TGender = "female" | "male" | "nonbinary";
export type TQuality = "veryLow" | "low" | "normal" | "high" | "veryHigh";
export interface IRecommended {
label: string;
name: string;
altNames?: string[];
language: string;
gender?: TGender | undefined;
age?: string | undefined;
quality: TQuality[];
recommendedPitch?: number | undefined;
recommendedRate?: number | undefined;
localizedName: string;
}
export declare const recommended: Array<IRecommended>;
export declare const quality: {
ca: {
normal: string;
high: string;
};
cs: {
normal: string;
high: string;
};
da: {
normal: string;
high: string;
};
de: {
normal: string;
high: string;
};
el: {
normal: string;
high: string;
};
en: {
normal: string;
high: string;
};
es: {
normal: string;
high: string;
};
fi: {
normal: string;
high: string;
};
fr: {
normal: string;
high: string;
};
hu: {
normal: string;
high: string;
};
hr: {
normal: string;
high: string;
};
it: {
normal: string;
high: string;
};
ja: {
normal: string;
high: string;
};
ko: {
normal: string;
high: string;
};
nb: {
normal: string;
high: string;
};
nl: {
normal: string;
high: string;
};
pl: {
normal: string;
high: string;
};
pt: {
normal: string;
high: string;
};
ro: {
normal: string;
high: string;
};
ru: {
normal: string;
high: string;
};
sk: {
normal: string;
high: string;
};
sl: {
normal: string;
high: string;
};
sv: {
normal: string;
high: string;
};
tr: {
normal: string;
high: string;
};
uk: {
normal: string;
high: string;
};
};
export declare const defaultRegion: {
bg: string;
ca: string;
cs: string;
da: string;
de: string;
en: string;
es: string;
eu: string;
fi: string;
fr: string;
gl: string;
hr: string;
hu: string;
it: string;
ja: string;
ko: string;
nb: string;
nl: string;
pl: string;
pt: string;
ro: string;
ru: string;
sk: string;
sl: string;
sv: string;
tr: string;
uk: string;
};
9 changes: 9 additions & 0 deletions build/cjs/data.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions build/cjs/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as voicesSelection from "./voices.js";
27 changes: 27 additions & 0 deletions build/cjs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.voicesSelection = void 0;
exports.voicesSelection = __importStar(require("./voices.js"));
3 changes: 3 additions & 0 deletions build/cjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "commonjs"
}
46 changes: 46 additions & 0 deletions build/cjs/voices.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { TGender, TQuality, IRecommended } from "./data.js";
export interface IVoices {
label: string;
voiceURI: string;
name: string;
language: string;
gender?: TGender | undefined;
age?: string | undefined;
offlineAvailability: boolean;
quality?: TQuality | undefined;
pitchControl: boolean;
recommendedPitch?: number | undefined;
recommendedRate?: number | undefined;
}
export declare function getSpeechSynthesisVoices(): Promise<SpeechSynthesisVoice[]>;
export declare function parseSpeechSynthesisVoices(speechSynthesisVoices: SpeechSynthesisVoice[]): IVoices[];
export declare function filterOnOfflineAvailability(voices: IVoices[], offline?: boolean): IVoices[];
export declare function filterOnGender(voices: IVoices[], gender: TGender): IVoices[];
export declare function filterOnLanguage(voices: IVoices[], language: string | string[]): IVoices[];
export declare function filterOnQuality(voices: IVoices[], quality: TQuality | TQuality[]): IVoices[];
export declare function filterOnNovelty(voices: IVoices[]): IVoices[];
export declare function filterOnVeryLowQuality(voices: IVoices[]): IVoices[];
export type TReturnFilterOnRecommended = [voicesRecommended: IVoices[], voicesLowerQuality: IVoices[]];
export declare function filterOnRecommended(voices: IVoices[], _recommended?: IRecommended[]): TReturnFilterOnRecommended;
export declare function sortByQuality(voices: IVoices[]): IVoices[];
export declare function sortByName(voices: IVoices[]): IVoices[];
export declare function sortByGender(voices: IVoices[], genderFirst: TGender): IVoices[];
export declare function sortByLanguage(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): IVoices[];
export declare function sortByRegion(voices: IVoices[], preferredRegions?: string[] | string, localization?: string | undefined): IVoices[];
export interface ILanguages {
label: string;
code: string;
count: number;
}
export declare function listLanguages(voices: IVoices[], localization?: string | undefined): ILanguages[];
export declare function listRegions(voices: IVoices[], localization?: string | undefined): ILanguages[];
export type TGroupVoices = Map<string, IVoices[]>;
export declare function groupByLanguages(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): TGroupVoices;
export declare function groupByRegions(voices: IVoices[], preferredRegions?: string[] | string, localization?: string | undefined): TGroupVoices;
export declare function groupByKindOfVoices(allVoices: IVoices[]): TGroupVoices;
export declare function getLanguages(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): ILanguages[];
/**
* Parse and extract SpeechSynthesisVoices,
* @returns IVoices[]
*/
export declare function getVoices(preferredLanguage?: string[] | string, localization?: string): Promise<IVoices[]>;
Loading

0 comments on commit c7630f5

Please sign in to comment.