Skip to content

Commit

Permalink
Merge pull request #6 from cadia-lvl/polly
Browse files Browse the repository at this point in the history
Polly
  • Loading branch information
staffanru authored Sep 16, 2022
2 parents fbf6a0f + 8268eba commit 75e6baf
Show file tree
Hide file tree
Showing 12 changed files with 511 additions and 76 deletions.
6 changes: 6 additions & 0 deletions constants/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ const WEBRICE_KEYS = {
PITCH_DEFAULT: 'webrice_default',
SUBSTITUTIONS: 'webrice_substitutions',
VOLUME: 'webrice_volume',
AWS_CREDS: 'webrice_aws_creds',
};

const BACKENDS = {
TIRO: 'tiro',
POLLY: 'polly',
};
49 changes: 39 additions & 10 deletions content/audioplayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class AudioPlayer {
return !this.audio.paused;
};

setupPlayer = (requests, text, playbackRate, voice) => {
setupPlayer = (backend, requests, text, playbackRate, voice) => {
this.stop();
// Revoke old url
window.URL.revokeObjectURL(this.audio.src);
Expand All @@ -41,22 +41,51 @@ class AudioPlayer {
this.text = text;
this.voice = voice;
this.first = true;
this.backend = backend;

this.mediaSource.addEventListener('sourceopen', this.openSource);
};

openSource = async (_) => {
const sourceBuffer = this.mediaSource.addSourceBuffer('audio/mpeg');

for (const request of this.requests) {
try {
const response = await fetch(request.url, request.content);
const reader = response.body.getReader();
await this.stream(reader, sourceBuffer);
} catch (e) {
console.log(e.message);
return;
}
switch (this.backend) {
case BACKENDS.TIRO:
for (const request of this.requests) {
try {
const response = await fetch(request.url, request.content);
const reader = response.body.getReader();
await this.stream(reader, sourceBuffer);
} catch (e) {
console.log(e.message);
return;
}
}
break;
case BACKENDS.POLLY:
const polly = await getPolly();

for (const request of this.requests) {
const response = await requestPolly(polly, request);
const audioStream = response.AudioStream;
const uIntArray = new Uint8Array(audioStream);
const arrayBuffer = uIntArray.buffer;

await new Promise((resolve, reject) => {
sourceBuffer.appendBuffer(arrayBuffer);
sourceBuffer.onupdateend = () => {
if (this.first) {
this.play();
this.first = false;
}
resolve(true);
};
});
}
break;

default:
break;
}
this.mediaSource.endOfStream();
};
Expand Down
12 changes: 9 additions & 3 deletions content/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,19 @@ const play = async () => {
return 'SUCCESS';
}

const requests = getRequestHeaderAndContent(text, settings);
const result = getRequestHeaderAndContent(text, settings);

if (requests.length == 0) {
if (result.requests.length == 0) {
return 'Unable to formulate tts requests.';
}

player.setupPlayer(requests, text, settings.playbackRate, settings.voice);
player.setupPlayer(
result.backend,
result.requests,
text,
settings.playbackRate,
settings.voice
);

await playing();

Expand Down
98 changes: 55 additions & 43 deletions content/fetch.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const DEFAULT_VOICE = "Alfur";
const DEFAULT_VOICE = 'Alfur';
const SPECIAL_VOICES = [
"Alfur",
"Dilja",
"Alfur_v2",
"Dilja_v2",
"Rosa",
"Bjartur",
'Alfur',
'Dilja',
'Alfur_v2',
'Dilja_v2',
'Rosa',
'Bjartur',
];
const AWS_VOICES = ['Dora', 'Karl'];
const MAX_REQUEST_SIZE = 300;

/**
Expand All @@ -18,17 +19,17 @@ const MAX_REQUEST_SIZE = 300;
*/
const normalizeText = (text, specialTrim = false) => {
let trimmed = text
.replace(/([\.\:\,\:]{0,1})\s{0,}\n+/gm, ". ") // replace all newlines and other special whitespaces with ". "
.replace(/\s+/gm, " "); // replace multiple spaces left with a single space
.replace(/([\.\:\,\:]{0,1})\s{0,}\n+/gm, '. ') // replace all newlines and other special whitespaces with ". "
.replace(/\s+/gm, ' '); // replace multiple spaces left with a single space

// For non-amazon voice extra care is needed
if (specialTrim) {
trimmed = trimmed
.replace(/(?<=\d)\s(?=\d)/gm, ", ") // replace digit space digit with digit comma space digit
.replace(/(?<=[\?\!])\./gm, ""); // replace remove dot from ?. or !.
.replace(/(?<=\d)\s(?=\d)/gm, ', ') // replace digit space digit with digit comma space digit
.replace(/(?<=[\?\!])\./gm, ''); // replace remove dot from ?. or !.

// If first characters are ". " then bad sound trim these away if so
if (trimmed.slice(0, 2) == ". ") {
if (trimmed.slice(0, 2) == '. ') {
trimmed = trimmed.slice(2);
}
}
Expand All @@ -39,7 +40,7 @@ const normalizeText = (text, specialTrim = false) => {

const output = [];
while (trimmed.length > MAX_REQUEST_SIZE) {
const lastSpace = trimmed.lastIndexOf(" ", MAX_REQUEST_SIZE);
const lastSpace = trimmed.lastIndexOf(' ', MAX_REQUEST_SIZE);
const section = trimmed.substring(0, lastSpace);
output.push(section);
trimmed = trimmed.slice(lastSpace);
Expand All @@ -55,38 +56,49 @@ const normalizeText = (text, specialTrim = false) => {
* @returns an array of requests, {url, content}
*/
const getRequestHeaderAndContent = (text, settings) => {
const url = "https://tts.tiro.is/v0/speech";
const audioType = "mp3";
const audioType = 'mp3';
const voiceName = settings?.voice ? settings.voice : DEFAULT_VOICE;
const specialTrim = SPECIAL_VOICES.includes(voiceName);
const normalizedTexts = normalizeText(text, specialTrim)

const normalizedTexts = normalizeText(text, specialTrim);
const awsVoice = AWS_VOICES.includes(settings?.voice);

const requests = normalizedTexts.map((text) => {
const request = {
url,
content: {

method: "POST",
mode: "cors",
headers: {
accept: "audio/mpeg",
"Content-Type": "application/json",
},
body: JSON.stringify({
Engine: "standard",
LanguageCode: "is-IS",
LexiconNames: [],
OutputFormat: audioType,
SampleRate: "16000",
SpeechMarkTypes: [],
Text: text,
TextType: "text",
VoiceId: voiceName,
}),
}
}
const request = awsVoice
? awsRequest(text, audioType, voiceName)
: tiroRequest(text, audioType, voiceName);
return request;
})
});

return { backend: awsVoice ? BACKENDS.POLLY : BACKENDS.TIRO, requests };
};

return requests;
}
const tiroRequest = (text, audioType, voiceName) => {
const url = 'https://tts.tiro.is/v0/speech';

return {
url,
content: {
method: 'POST',
mode: 'cors',
headers: {
accept: 'audio/mpeg',
'Content-Type': 'application/json',
},
body: JSON.stringify({
Engine: 'standard',
LanguageCode: 'is-IS',
LexiconNames: [],
OutputFormat: audioType,
SampleRate: '16000',
SpeechMarkTypes: [],
Text: text,
TextType: 'text',
VoiceId: voiceName,
}),
},
};
};

const awsRequest = (text, audioType, voiceName) => {
return pollyParams(text, audioType, voiceName);
};
7 changes: 5 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "WebRICE",
"version": "1.0.2",
"version": "1.0.3",

"description": "Text to speech service for icelandic",
"homepage_url": "https://www.webrice.is",
Expand All @@ -25,8 +25,11 @@
"matches": ["<all_urls>"],
"js": [
"constants/constants.js",
"content/audioplayer.js",
"utils/aws-sdk.js",
"utils/storage_helper.js",
"utils/aws-helper.js",
"content/fetch.js",
"content/audioplayer.js",
"content/content.js"
],
"run_at": "document_start"
Expand Down
80 changes: 75 additions & 5 deletions popup/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@

body {
background: whitesmoke;
font-family: 'Roboto', Ubuntu, Arial, sans-serif;
/* letter-spacing: 1px; */
font-size: 14px;
}

#webrice_container {
Expand Down Expand Up @@ -59,6 +62,12 @@ body {
width: var(--icon-size);
}

.webrice_icon_secondary {
fill: var(--main-color-one);
height: var(--icon-size);
width: var(--icon-size);
}

.webrice_select_container {
position: relative;
}
Expand Down Expand Up @@ -130,10 +139,6 @@ body {
gap: 0.5rem;
}

.webrice_hide {
display: none;
}

.webrice_rotate_90 {
transform: rotate(90deg);
}
Expand All @@ -151,7 +156,7 @@ body {
display: inherit;
}

.webrice_substitution {
.webrice_slider .webrice_substitution {
width: 100%;
display: flex;
align-items: center;
Expand Down Expand Up @@ -213,6 +218,10 @@ option {
width: 60%;
}

.webrice_slider > input {
width: 100%;
}

.webrice_substitution > input {
width: 40%;
}
Expand All @@ -227,3 +236,64 @@ option {
pointer-events: none;
opacity: 30%;
}

details > summary {
font-size: 1rem;
font-weight: bold;
}

.webrice_flex_column {
display: flex;
flex-direction: column;
}

.webrice_flex_row {
display: flex;
flex-direction: row;
}

.gap_small {
gap: 0.25rem;
}

.small_bottom_margin {
margin-bottom: 0.25rem;
}

.verification_item {
border-radius: 1rem;
background: white;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.2);
padding: 0.5rem;
gap: 1rem;
align-items: center;
font-weight: bold;
}

.verification_pending {
background-color: #f3f2f2;
display: flex;
align-items: center;
}

.verification_success {
background: #beffd3;
color: #03802b;
}

.verification_failed {
background: #ffc5c4;
color: #b30300;
}

.flex_start {
align-content: flex-start;
}

.webrice_hide {
display: none;
}

h3 {
font-size: 1rem;
}
Loading

0 comments on commit 75e6baf

Please sign in to comment.