Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Phoneme conversion presets #157

Merged
merged 18 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ If you are developing OSS projects related to singing voice synthesis, you may f

## Contributors

[sdercolin](https://github.com/sdercolin), [ghosrt](https://github.com/ghosrt)
, [shine5402](https://github.com/shine5402)
[sdercolin](https://github.com/sdercolin), [ghosrt](https://github.com/ghosrt), [shine5402](https://github.com/shine5402), [adlez27](https://github.com/adlez27)

## Localization help

Expand Down
2 changes: 1 addition & 1 deletion src/jsMain/kotlin/model/Format.kt
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ enum class Format(
io.Vsq.generate(project, features)
},
possibleLyricsTypes = listOf(RomajiCv, KanaCv),
availableFeaturesForGeneration = listOf(ConvertPitch),
availableFeaturesForGeneration = listOf(ConvertPitch, ConvertPhonemes),
adlez27 marked this conversation as resolved.
Show resolved Hide resolved
),
VocaloidMid(
"mid",
Expand Down
10 changes: 10 additions & 0 deletions src/jsMain/kotlin/model/PhonemesMappingPreset.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package model

import process.phonemes.PhonemesMappingRequest

class PhonemesMappingPreset(
val sourceFormats: List<Format>,
val targetFormats: List<Format>,
val name: String,
val phonemesMap: PhonemesMappingRequest,
)
210 changes: 196 additions & 14 deletions src/jsMain/kotlin/process/phonemes/PhonemesMapping.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package process.phonemes

import external.require
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import model.Format
import model.Note
import model.PhonemesMappingPreset
import model.Project
import model.Track
import process.validateNotes
Expand All @@ -28,12 +31,189 @@ data class PhonemesMappingRequest(

companion object {

fun findPreset(name: String) = Presets.find { it.first == name }?.second
fun findPreset(name: String) = Presets.find { it.name == name }?.phonemesMap

fun getPreset(name: String) = requireNotNull(findPreset(name))

val Presets: List<Pair<String, PhonemesMappingRequest>> by lazy {
listOf()
val Presets: List<PhonemesMappingPreset> by lazy {
listOf(
PhonemesMappingPreset(
sourceFormats = listOf(Format.Svp),
targetFormats = Format.vocaloidFormats,
name = "Japanese (SynthV to Vocaloid)",
phonemesMap = PhonemesMappingRequest(
require("./texts/SynthV JA to Vocaloid JA.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = Format.vocaloidFormats,
targetFormats = listOf(Format.Svp),
name = "Japanese (Vocaloid to SynthV)",
phonemesMap = PhonemesMappingRequest(
require("./texts/Vocaloid JA to SynthV JA.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Svp),
targetFormats = Format.vocaloidFormats,
name = "English (SynthV to Vocaloid)",
phonemesMap = PhonemesMappingRequest(
require("./texts/SynthV EN to Vocaloid EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Svp),
targetFormats = listOf(Format.Ustx),
name = "English: ARPAsing (SynthV to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/SynthV EN to OpenUtau ARPAsing.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Svp),
targetFormats = listOf(Format.Ustx),
name = "English: X-SAMPA (SynthV to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/SynthV EN to OpenUtau X-SAMPA EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Svp),
targetFormats = listOf(Format.Ustx),
name = "English: VCCV (SynthV to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/SynthV EN to OpenUtau VCCV.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = Format.vocaloidFormats,
targetFormats = listOf(Format.Svp),
name = "English (Vocaloid to SynthV)",
phonemesMap = PhonemesMappingRequest(
require("./texts/Vocaloid EN to SynthV EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = Format.vocaloidFormats,
targetFormats = listOf(Format.Ustx),
name = "English: ARPAsing (Vocaloid to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/Vocaloid EN to OpenUtau ARPAsing.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = Format.vocaloidFormats,
targetFormats = listOf(Format.Ustx),
name = "English: X-SAMPA (Vocaloid to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/Vocaloid EN to OpenUtau X-SAMPA EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = Format.vocaloidFormats,
targetFormats = listOf(Format.Ustx),
name = "English: VCCV (Vocaloid to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/Vocaloid EN to OpenUtau VCCV.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Svp),
name = "English: ARPAsing (OpenUtau to SynthV)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau ARPAsing to SynthV EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = Format.vocaloidFormats,
name = "English: ARPAsing (OpenUtau to Vocaloid)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau ARPAsing to Vocaloid EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Ustx),
name = "English: ARPAsing to X-SAMPA (OpenUtau to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau ARPAsing to OpenUtau X-SAMPA EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Ustx),
name = "English: ARPAsing to VCCV (OpenUtau to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau ARPAsing to OpenUtau VCCV.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Svp),
name = "English: X-SAMPA (OpenUtau to SynthV)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau X-SAMPA EN to SynthV EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = Format.vocaloidFormats,
name = "English: X-SAMPA (OpenUtau to Vocaloid)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau X-SAMPA EN to Vocaloid EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Ustx),
name = "English: X-SAMPA to ARPAsing (OpenUtau to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau X-SAMPA EN to OpenUtau ARPAsing.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Ustx),
name = "English: X-SAMPA to VCCV (OpenUtau to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau X-SAMPA EN to OpenUtau VCCV.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Svp),
name = "English: VCCV (OpenUtau to SynthV)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau VCCV to SynthV EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = Format.vocaloidFormats,
name = "English: VCCV (OpenUtau to Vocaloid)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau VCCV to Vocaloid EN.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Ustx),
name = "English: VCCV to ARPAsing (OpenUtau to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau VCCV to OpenUtau ARPAsing.txt").default as String,
),
),
PhonemesMappingPreset(
sourceFormats = listOf(Format.Ustx),
targetFormats = listOf(Format.Ustx),
name = "English: VCCV to X-SAMPA (OpenUtau to OpenUtau)",
phonemesMap = PhonemesMappingRequest(
require("./texts/OpenUtau VCCV to OpenUtau X-SAMPA EN.txt").default as String,
),
),
)
}
}
}
Expand All @@ -48,20 +228,22 @@ fun Track.replacePhonemes(request: PhonemesMappingRequest?) = copy(

fun Note.replacePhonemes(request: PhonemesMappingRequest?): Note {
if (request == null) return copy(phoneme = null)
var output = phoneme?.split(" ") ?: return this
val rawInput = phoneme?.split(" ") ?: return this
val input = rawInput.map { it to true }.toMutableList() // boolean represents the phoneme is not converted yet
var output = mutableListOf<Pair<String, Int>>() // int represents index from input phoneme list
for ((key, value) in request.map) {
val keySplit = key.split(" ")
for (i in 0..(output.size - keySplit.size)) {
val subList = output.subList(i, i + keySplit.size)
if (subList == keySplit) {
output = output.subList(0, i) + value.split(" ") +
output.subList(i + keySplit.size, output.size)
// once replaced, the count of output is changed, so we could not proceed the replacement.
// this means that multiple occurrences of the same phoneme set in one note get replaced only once.
break
for (i in 0..(input.size - keySplit.size)) {
val subList = input.subList(i, i + keySplit.size)
if (subList.map { it.first } == keySplit && subList.map { it.second }.all { it }) {
output += value to i
for (j in 0 until subList.size) {
subList[j] = subList[j].first to false
}
}
}
}
output = output.filter { it.isNotBlank() }
return copy(phoneme = output.joinToString(" "))
output += input.mapIndexedNotNull { index, pair -> if (pair.second) pair.first to index else null }
output = output.filter { it.first.isNotBlank() }.sortedBy { it.second }.toMutableList()
return copy(phoneme = output.joinToString(" ") { it.first })
}
2 changes: 2 additions & 0 deletions src/jsMain/kotlin/ui/ConfigurationEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ val ConfigurationEditor = scopedFC<ConfigurationEditorProps> { props, scope ->
submitState = setLyricsMapping
}
if (phonemesConversion.isAvailable) PhonemesConversionBlock {
this.sourceFormat = props.projects.first().format
this.targetFormat = props.outputFormat
initialState = phonemesConversion
submitState = setPhonemesConversion
}
Expand Down
26 changes: 18 additions & 8 deletions src/jsMain/kotlin/ui/configuration/PhonemesConversion.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import csstype.em
import csstype.px
import emotion.react.css
import kotlinx.js.jso
import model.Format
import mui.icons.material.HelpOutline
import mui.material.BaseTextFieldProps
import mui.material.Button
Expand Down Expand Up @@ -40,9 +41,12 @@ import ui.common.subFC
import ui.strings.Strings
import ui.strings.string

external interface PhonemesConversionProps : SubProps<PhonemesConversionState>
external interface PhonemesConversionProps : SubProps<PhonemesConversionState> {
var sourceFormat: Format
var targetFormat: Format
}

val PhonemesConversionBlock = subFC<PhonemesConversionProps, PhonemesConversionState> { _, state, editState ->
val PhonemesConversionBlock = subFC<PhonemesConversionProps, PhonemesConversionState> { props, state, editState ->
FormGroup {
div {
configurationSwitch(
Expand All @@ -53,10 +57,11 @@ val PhonemesConversionBlock = subFC<PhonemesConversionProps, PhonemesConversionS
}
}

if (state.isOn) buildPhonemesConversionDetail(state, editState)
if (state.isOn) buildPhonemesConversionDetail(props, state, editState)
}

private fun ChildrenBuilder.buildPhonemesConversionDetail(
props: PhonemesConversionProps,
state: PhonemesConversionState,
editState: (PhonemesConversionState.() -> PhonemesConversionState) -> Unit,
) {
Expand Down Expand Up @@ -138,12 +143,17 @@ private fun ChildrenBuilder.buildPhonemesConversionDetail(
)
}
}
PhonemesMappingRequest.Presets.forEach { preset ->
MenuItem {
value = preset.first
+(preset.first)
PhonemesMappingRequest.Presets
.filter { preset ->
props.sourceFormat in preset.sourceFormats + Format.UfData &&
props.targetFormat in preset.targetFormats + Format.UfData
}
.forEach { preset ->
MenuItem {
value = preset.name
+(preset.name)
}
}
}
}
}
Button {
Expand Down
28 changes: 28 additions & 0 deletions src/jsMain/resources/texts/OpenUtau ARPAsing to OpenUtau VCCV.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
aa=a
ae=@
ah=u
ao r=0 r
ao=9
aw=8
ay=I
eh=e
er=3
ey=A
hh iy=hh E
hh y=hh y
hh=h
ih=i
iy=E
jh=j
ow l=0 l
ow=O
oy=Q
s k=sk
s p=sp
s t=st
s m=sm
s n=sn
uh=6
uw=o
ae n=& n
ae m=& m
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
aa=A
ae={
ah=V
ao=O
aw=aU
ay=aI
ch=tS
dh=D
eh=E
er=3
ey=eI
hh=h
ih=I
iy=i
jh=dZ
ng=N
ow=oU
oy=OI
sh=S
th=T
uh=U
uw=u
zh=Z
y=j
2 changes: 2 additions & 0 deletions src/jsMain/resources/texts/OpenUtau ARPAsing to SynthV EN.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
d r=dr
t r=tr
Loading
Loading