diff --git a/packages/components/src/api/voice_conversion/EnqueueVoiceConversion.ts b/packages/components/src/api/voice_conversion/EnqueueVoiceConversion.ts index 3ddebe8163..eda5bb3187 100644 --- a/packages/components/src/api/voice_conversion/EnqueueVoiceConversion.ts +++ b/packages/components/src/api/voice_conversion/EnqueueVoiceConversion.ts @@ -1,9 +1,19 @@ import { ApiConfig } from "@storyteller/components"; +export enum EnqueueVoiceConversionFrequencyMethod { + Crepe = "crepe", + Dio = "dio", + Harvest = "harvest", +} + export interface EnqueueVoiceConversionRequest { uuid_idempotency_token: string, voice_conversion_model_token: string, source_media_upload_token: string, + + // Optional args + override_f0_method?: EnqueueVoiceConversionFrequencyMethod, + transpose?: number, } export interface EnqueueVoiceConversionSuccessResponse { diff --git a/packages/fakeyou.com/package.json b/packages/fakeyou.com/package.json index 9894a475f0..e77693211e 100644 --- a/packages/fakeyou.com/package.json +++ b/packages/fakeyou.com/package.json @@ -45,6 +45,7 @@ "react-dom": "^16.13.1", "react-drag-drop-files": "^2.3.9", "react-i18next": "^11.15.4", + "react-range": "^1.8.14", "react-reveal": "^1.2.2", "react-router-dom": "^5.2.0", "react-scripts": "5.0.0", diff --git a/packages/fakeyou.com/src/scss/site-refresh.scss b/packages/fakeyou.com/src/scss/site-refresh.scss index 39869d3a4d..23e543f77b 100644 --- a/packages/fakeyou.com/src/scss/site-refresh.scss +++ b/packages/fakeyou.com/src/scss/site-refresh.scss @@ -724,6 +724,10 @@ styles with important tags: box-shadow: none; cursor: pointer; + &.no-padding { + padding-left: 0.5rem; + } + &:hover { background-color: lighten($input-dark, 5%); border-color: lighten($input-dark, 5%) !important; @@ -866,6 +870,13 @@ styles with important tags: fill: lighten($secondary, 5%); } + .tippy-box[data-theme~="range-slider"] { + margin-top: -5px; + background-color: lighten($panel, 10%); + color: $white; + font-size: 13px !important; + } + .sliding-content { overflow: hidden; opacity: 0; @@ -1269,6 +1280,19 @@ styles with important tags: .alert-info { font-size: 14px; } + + .range-slider-value { + text-align: center; + max-width: 50px; + padding: 6px; + border-radius: 8px; + pointer-events: none; + background-color: lighten($panel, 5%) !important; + border-width: 1px; + border-color: rgba(255, 255, 255, 0.12) !important; + color: rgba($white, 0.85) !important; + font-weight: 500 !important; + } } select { @@ -1404,6 +1428,10 @@ textarea { height: 450px; } + .session-vc-section { + height: 565px; + } + .session-tts-section::-webkit-scrollbar { width: 10px; } diff --git a/packages/fakeyou.com/src/v2/view/pages/vc/vc_model_list/VcModelListPage.tsx b/packages/fakeyou.com/src/v2/view/pages/vc/vc_model_list/VcModelListPage.tsx index 90efaae98a..f1229f3e4c 100644 --- a/packages/fakeyou.com/src/v2/view/pages/vc/vc_model_list/VcModelListPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/vc/vc_model_list/VcModelListPage.tsx @@ -23,6 +23,7 @@ import { import { VcModelListSearch } from "./components/VcModelListSearchComponent"; import { EnqueueVoiceConversion, + EnqueueVoiceConversionFrequencyMethod, EnqueueVoiceConversionIsSuccess, EnqueueVoiceConversionRequest, } from "@storyteller/components/src/api/voice_conversion/EnqueueVoiceConversion"; @@ -33,6 +34,8 @@ import { } from "@storyteller/components/src/jobs/InferenceJob"; import { SessionVoiceConversionResultsList } from "../../../_common/SessionVoiceConversionResultsList"; import { Link } from "react-router-dom"; +import PitchShiftComponent from "./components/PitchShiftComponent"; +import PitchEstimateMethodComponent from "./components/PitchEstimateMethodComponent"; interface Props { sessionWrapper: SessionWrapper; @@ -58,6 +61,7 @@ interface Props { function VcModelListPage(props: Props) { usePrefixedDocumentTitle("Voice Conversion"); + const [convertLoading, setConvertLoading] = useState(false); const [canConvert, setCanConvert] = useState(false); @@ -69,6 +73,11 @@ function VcModelListPage(props: Props) { uuidv4() ); + const [maybeF0MethodOverride, setMaybeF0MethodOverride] = + useState(undefined); + + const [semitones, setSemitones] = useState(0); + // NB: Something of a UI hack here. // The 3rd party microphone component doesn't let you clear it, so we emulate form clearing // with this variable. @@ -147,6 +156,14 @@ function VcModelListPage(props: Props) { source_media_upload_token: mediaUploadToken, }; + if (semitones !== 0) { + request.transpose = semitones; + } + + if (maybeF0MethodOverride !== undefined) { + request.override_f0_method = maybeF0MethodOverride; + } + Analytics.voiceConversionGenerate( props.maybeSelectedVoiceConversionModel.token ); @@ -167,6 +184,16 @@ function VcModelListPage(props: Props) { ev.preventDefault(); }; + const handlePitchChange = (value: any) => { + setSemitones(value); + changeConvertIdempotencyToken(); + }; + + const handlePitchMethodChange = (value: any) => { + setMaybeF0MethodOverride(value); + changeConvertIdempotencyToken(); + }; + const speakButtonClass = convertLoading ? "btn btn-primary w-100 disabled" : "btn btn-primary w-100"; @@ -280,6 +307,26 @@ function VcModelListPage(props: Props) { +
+ +
+
+ +
+
+ +
+
+
{/*
+
+ +
+
+ +
+
+ +
+
+
+
@@ -381,7 +449,7 @@ function VcModelListPage(props: Props) { /> Session V2V Results -
+
void; +} + +const dropdownFieldClass = { + control: (state: any) => + state.isFocused + ? "select-search no-padding focused rounded" + : "select-search no-padding rounded", + option: (state: any) => (state.isFocused ? "select-option" : "select-option"), + input: (state: any) => (state.isFocused ? "select-input" : "select-input"), + placeholder: (state: any) => + state.isFocused ? "select-placeholder" : "select-placeholder", + singleValue: (state: any) => + state.isFocused ? "select-value" : "select-value", + menu: (state: any) => + state.isFocused ? "select-container" : "select-container", + indicatorSeparator: (state: any) => + state.isFocused ? "select-separator" : "select-separator", +}; + +function PitchEstimateMethodComponent(props: PitchShiftProps) { + + const handleMethodChange = (option: any, actionMeta: any) => { + const pitchMethodName = option?.value; + let pitchMethod = undefined; + + switch (pitchMethodName) { + case "crepe": + pitchMethod = EnqueueVoiceConversionFrequencyMethod.Crepe; + break; + case "harvest": + pitchMethod = EnqueueVoiceConversionFrequencyMethod.Harvest; + break; + case "dio": + pitchMethod = EnqueueVoiceConversionFrequencyMethod.Dio; + break; + } + + props.onMethodChange(pitchMethod); + }; + + interface DropdownOption { + label: string; + value: EnqueueVoiceConversionFrequencyMethod; + } + + const options : DropdownOption[] = [ + { label: "CREPE", value: EnqueueVoiceConversionFrequencyMethod.Crepe }, + { label: "Harvest", value: EnqueueVoiceConversionFrequencyMethod.Harvest }, + { label: "DIO", value: EnqueueVoiceConversionFrequencyMethod.Dio }, + ]; + + const selectedOption = options.find((option) => option.value === props.pitchMethod) || options[0]; + + return ( + <> +
+ +
+ + ); +} + +export default PitchShiftComponent; diff --git a/yarn.lock b/yarn.lock index e30ef8cf87..f2e6a909b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4498,6 +4498,7 @@ __metadata: dependencies: craco: ^0.0.3 lerna: ^4.0.0 + react-range: ^1.8.14 yarn: ^1.22.19 languageName: unknown linkType: soft @@ -17132,6 +17133,16 @@ __metadata: languageName: node linkType: hard +"react-range@npm:^1.8.14": + version: 1.8.14 + resolution: "react-range@npm:1.8.14" + peerDependencies: + react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + react-dom: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 7020c011dc293db46ede9b7cb014dbfe71d59f96aad9cf9b12e9de26c2b600c504e11d39368af8c539c4a3dc1f93e966745063e0b494c388c28fbadd338164bb + languageName: node + linkType: hard + "react-reconciler@npm:0.25.1": version: 0.25.1 resolution: "react-reconciler@npm:0.25.1"