Modal test frontend can hit /speak or /advanced_tts

This commit is contained in:
Brandon Thomas committed 2020-05-27 00:17:10 -04:00
1 parent ac1a9281d4
commit 3ce772cd80
10 files changed
+275 -7

No files matched your search

+3
View File
@@ -17,6 +17,9 @@
},
"scripts": {
"start": "PORT=12345 react-scripts start",
"start7000": "PORT=7000 react-scripts start",
"start8000": "PORT=8000 react-scripts start",
"start8080": "PORT=8080 react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
+1 -1
View File
@@ -28,7 +28,7 @@ class TtsResponse {
let makeTtsRequest = (sentence: string): Promise<TtsResponse> => {
const request = new TtsRequest(sentence);
const url = `/tts`;
const url = `http://localhost:12345/advanced_tts`;
return fetch(url, {
method: 'POST',
+52 -3
View File
@@ -1,14 +1,19 @@
import React from 'react';
import Howl from 'howler';
import TextInput from './TextInput';
import TrackList from './TrackList';
import { ModelPickerDropdownComponent } from './model_picker/ModelPickerDropdownComponent';
import { SpeakerModeComponent } from './modes/speaker_mode/SpeakerModeComponent';
enum Mode {
SPEAKER,
ADVANCED,
}
interface Props {
}
interface State {
utterances: Array<TextAudioPair>
mode: Mode,
}
class TextAudioPair {
@@ -26,7 +31,10 @@ class MainComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { utterances: new Array() };
this.state = {
utterances: new Array(),
mode: Mode.ADVANCED,
};
}
/** Add a new audio track. */
@@ -38,7 +46,40 @@ class MainComponent extends React.Component<Props, State> {
this.setState({utterances: utterances});
}
changeMode = () => {
let nextMode;
switch (this.state.mode) {
case Mode.ADVANCED:
nextMode = Mode.SPEAKER;
break;
case Mode.SPEAKER:
nextMode = Mode.ADVANCED;
break;
}
this.setState({
mode: nextMode
});
}
public render() {
let component;
if (this.state.mode == Mode.ADVANCED) {
component = this.renderAdvancedMode();
} else {
component = this.renderSpeakerMode();
}
return (
<div>
<button onClick={this.changeMode}>Change Mode</button>
<hr />
{component}
</div>
)
}
public renderAdvancedMode() {
return (
<div>
<TextInput appendUtteranceCallback={this.appendUtterance} />
@@ -46,6 +87,14 @@ class MainComponent extends React.Component<Props, State> {
</div>
);
}
public renderSpeakerMode() {
return (
<div>
<SpeakerModeComponent></SpeakerModeComponent>
</div>
);
}
}
export {TextAudioPair, MainComponent};
+1 -1
View File
@@ -70,7 +70,7 @@ class TextInput extends React.Component<Props, State> {
request.arpabet_tacotron_model = this.state.arpabet_tacotron_model || "";
request.melgan_model = this.state.melgan_model || "";
const url = '/tts';
const url = 'http://localhost:12345/advanced_tts';
fetch(url, {
method: 'POST',
headers: {
@@ -26,7 +26,7 @@ class ModelPickerDropdownComponent extends React.Component<Props, State> {
}
public loadModels() {
const url = '/models';
const url = 'http://localhost:12345/models';
fetch(url)
.then(res => res.json())
.then(
@@ -0,0 +1,93 @@
import React from 'react';
import Howl from 'howler';
interface Props {
speaker?: String,
}
interface State {
text?: String,
howl?: Howl,
}
/// Requests to the "speak" endpoint.
class SpeakRequest {
text: String
speaker: String
constructor(text: String, speaker: String) {
this.text = text;
this.speaker = speaker;
}
}
class SpeakerAudioForm extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
text: undefined,
howl: undefined,
};
}
componentDidMount() {
// OnMount events. An async CTOR, essentially.
}
handleTextChange = (ev: React.FormEvent<HTMLInputElement>) => {
const text = (ev.target as HTMLInputElement).value;
this.setState({text: text});
}
makeRequest = (ev: React.FormEvent<HTMLFormElement>) => {
console.log("Form Submit");
if (!this.state.text) {
return;
}
let request = new SpeakRequest(this.state.text, this.props.speaker!);
const url = 'http://localhost:12345/speak';
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
})
.then(res => res.blob())
.then(blob => {
console.log(blob);
const url = window.URL.createObjectURL(blob);
console.log(url);
const sound = new Howl.Howl({
src: [url],
format: 'wav',
});
this.setState({howl: sound});
sound.play();
(window as any).sound = sound;
});
ev.preventDefault();
return false;
}
public render() {
return (
<form onSubmit={this.makeRequest}>
<input onChange={this.handleTextChange} />
<button>Submit</button>
</form>
);
}
}
export default SpeakerAudioForm;
@@ -0,0 +1,36 @@
import React from 'react';
import { SpeakerResponse, SpeakerDetails } from './SpeakerResponse';
interface Props {
changeCallback: (ev: React.FormEvent<HTMLSelectElement>) => void,
speakers: SpeakerDetails[]
}
interface State {
}
class SpeakerDropdownComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
}
public render() {
return (
<div>
<strong>Select Speaker</strong>
<br />
<select onChange={this.props.changeCallback}>
{this.props.speakers.map(function(speaker: SpeakerDetails) {
let name : string = speaker.name || "unknown";
let slug : string = speaker.slug || "unknown";
return (<option value={slug}>{name}</option>)
})};
</select>
</div>
);
}
}
export {SpeakerDropdownComponent};
@@ -0,0 +1,72 @@
import React from 'react';
import { SpeakerDetails, SpeakerResponse } from './SpeakerResponse';
import { SpeakerDropdownComponent } from './SpeakerDropdownComponent';
import SpeakerAudioForm from './SpeakerAudioForm';
interface Props {
}
interface State {
speaker_response?: SpeakerResponse,
speaker_slug?: string,
}
class SpeakerModeComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
speaker_response: undefined,
speaker_slug: undefined,
};
}
componentDidMount() {
this.loadSpeakers();
}
public loadSpeakers() {
const url = 'http://localhost:12345/speakers';
fetch(url)
.then(res => res.json())
.then(
(result) => {
console.log('loaded speakers', result);
let first_speaker = result.speakers[0].slug;
this.setState({
speaker_response: result,
speaker_slug: first_speaker,
})
}
);
}
changeSpeaker = (ev: React.FormEvent<HTMLSelectElement>) => {
const speakerSlug = (ev.target as HTMLInputElement).value;
this.setState({
speaker_slug: speakerSlug,
})
}
public render() {
let speakers = this.state.speaker_response?.speakers;
let dropdown;
if (speakers?.length || 0 > 0) {
dropdown = <SpeakerDropdownComponent
speakers={speakers!}
changeCallback={this.changeSpeaker}
/>
}
return (
<div>
<b>SpeakerMode</b>
{dropdown}
<SpeakerAudioForm speaker={this.state.speaker_slug!} />
</div>
);
}
}
export {SpeakerModeComponent};
@@ -0,0 +1,15 @@
class SpeakerResponse {
speakers?: SpeakerDetails[]
}
class SpeakerDetails {
name?: string
slug?: string
model_pipeline?: string
tacotron?: string
melgan?: string
}
export {SpeakerResponse, SpeakerDetails};
+1 -1
View File
@@ -101,7 +101,7 @@ async fn main() -> std::io::Result<()> {
.allowed_origin("http://trumped.com")
.allowed_origin("http://jungle.horse")
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
.allowed_headers(vec![http::header::ACCEPT])
.allowed_header(http::header::CONTENT_TYPE)
.max_age(3600)
.finish())