• If you do not recieve your confirmation email within a few hours, please email haloutau@gmail.com with your username for manual validation. Your account should be activated within 24 hours.
    You may also reach out via any other listed contact on Admin Halo's about page: https://utaforum.net/members/halo.194/#about
Resource icon

How to Create Base OTOs (Part 3)

A comprehensive guide on writing base configuration files (otos) for Classic UTAU and OpenUTAU. Also applicable to DIY synths that use the same configuration parameters. This resource assumes at least a basic knowledge of UTAU.

This tutorial is crossposted from my website. Part 1 covers making bases from scratch and Part 2 covers making bases from finished voicebanks.



Making Base OTOs Using Regular Expressions

Regular expressions (regex) are a more advanced method of matching characters in text strings. Relevant here, these can speed up the process of base oto creation by allowing us to generate entire sections of our reclist with much less manual editing. This can be done in any text editor that allows the use of regex in find and replace operations; I will be using Notepad++ to demonstrate.

First, I will go over two useful things you can do with regex even if you don't plan to use it to create the entire base, then I will cover how to use it to generate complete bases. You can also do a lot more with regex than what is outlined here, too; here's a quick reference for regex operations if you want to experiment with them.

To start with, copy the reclist you want to use to build your base into a new, completely empty oto.ini file. Remember to encode in Shift JIS if you plan to use Japanese characters.

Be sure to remove any empty lines from the reclist before performing regex operations; blank lines in the oto.ini will cause errors. You can do this by going to Edit > Line Operations > Remove Empty Lines in Notepad++.

baseoto4.jpg




Appending Text To Every Line

One of the most useful features of regex is the ability to append additional characters to every line. Let's use a section of a hypothetical CVVC reclist to demonstrate:
Code:
ka-kak
ki-kik
cha-chach
chi-chich

Since this is an oto file, we need each line to end with, at minimum, [.wav=,,,,,], and we can very easily attach this to every string using regex.

Press <CTRL+H> on your keyboard to open up the find and replace menu. Under Search mode, make sure that Regular expression is selected and that . matches new line is disabled.

baseoto3.jpg


In the Find what field, enter a [$] symbol. This will match the end of every line.

Note said:
I am using square brackets [] here to make it more obvious what should be entered inside the text field; these should not be included in your actual operation.

In the Replace with field, enter [.wav=,,,,,]. Make sure there are exactly 5 commas after the equals sign.

Click Replace all to append this string to every line of the oto. Now, the oto.ini has valid formatting, though its alias and parameters are empty.
Code:
ka-kak.wav=,,,,,
ki-kik.wav=,,,,,
cha-chach.wav=,,,,,
chi-chich.wav=,,,,,

To append text to the start of every line instead, such as for adding a string prefix for file organization, match [^] in the Find field instead of [$].

To perform this or any other operation on only one section of a reclist, highlight the section you wish to edit and make sure that In selection is enabled in the find and replace menu.



Duplicating Lines

The next basic operation you'll likely want to perform is line duplication. Since you'll more than likely be pulling multiple samples from each string, this can save you the tediousness of duplicating all of the lines manually.

To duplicate every line of the reclist once, Find [^(.*)$] and Replace with [$1\r$1]. To duplicate every line multiple times, you can add additional [\r$1] to the Replace field. For example, [$1\r$1\r$1\r$1] will result in four instances of each string.
Code:
ka-kak.wav=,,,,,
ka-kak.wav=,,,,,
ka-kak.wav=,,,,,
ka-kak.wav=,,,,,
ki-kik.wav=,,,,,
ki-kik.wav=,,,,,
ki-kik.wav=,,,,,
ki-kik.wav=,,,,,
cha-chach.wav=,,,,,
cha-chach.wav=,,,,,
cha-chach.wav=,,,,,
cha-chach.wav=,,,,,
chi-chich.wav=,,,,,
chi-chich.wav=,,,,,
chi-chich.wav=,,,,,
chi-chich.wav=,,,,,

Make sure the Replace string ends with [$1] and not [\r] to avoid adding extra blank lines, and make sure you are using backslash [\] not forwardslash [/].

As mentioned, the [^] and [$] symbols match with the beginning and end of each line, respectively. The [.] symbol (also called wildcard) matches any character, and the [*] symbol is a quantifier that matches zero or more of the previous character — which, in this case, is anything. In other words, we are telling regex to match every character from every line.

The parentheses [()] create an index of a particular section of the matched string called a substring. In this case, we want to index the entire string so that we can duplicate it in our Replace operation.

Each indexed substring is indicated with a [$] symbol followed by a number, starting with 1 and increasing numerically for each additional substring. Since we only have one substring, we only have [$1] to work with.

[\r] is a return character equivalent to hitting <ENTER> on your keyboard. I prefer using this to newline character [\n] to make sure its read correctly as a linebreak.

We can also combine this and the previous operation by using a longer Replace string. To achieve the same result as the above using a single operation, Find [^(.*)$] to match every line of the reclist (or every line of a highlighted section), and Replace with [$1.wav=,,,,,\r$1.wav=,,,,,\r$1.wav=,,,,,\r$1.wav=,,,,,]. What we've done here is simply insert [.wav=,,,,,] after every indexed substring and before every return character, resulting in it being appended to every duplicated line.



Generating Complete Bases

In order to use regex to generate most or all of the base for you, you first need to know what the finalized base should look like. You should know how many samples will be taken from each line, how their aliases should be formatted, and what their parameter values should be — basically, all of the things covered earlier in this tutorial.

Following this, the only part that's more complicated than the above is knowing which parts of the strings to index in order to build the aliases and knowing how to utilize wildcard characters to account for string variation.

The same basic logic shown here will apply to a reclist in any language or format, though alias generation is mostly limited to instances where both the filename and alias use Latin characters. Kana or other non-Latin characters will most likely have to be done more manually. That said, regex can still be used to build the majority of the base.

Lastly, regex base generation won't work as well for reclists that aren't symmetrical — that is, reclists where different strings have different lengths or phoneme patterns. It also will not look for exceptions, for example if some consonants have initial C's and others don't.

There are a few solutions to this. What works best will depend on your specific reclist:
  • Generating the pattern that most strings will follow, then manually editing the ones that are different
  • Generating the reclist by matching each section of symmetrical strings separately rather than trying to do it all in one operation
    • This can sometimes be faster than trying to work out a single operation to do everything, and sometimes that isn't even possible
  • Using a combination of regex and manual copy-paste-find-replace as needed
Let's consider some strings from a typical Japanese VCV reclist:
Code:
ka-ka-ki-ka-ku-ka-N-ka
ki-ki-ku-ki-ke-ki-N-ki
ku-ku-ke-ku-ko-ku-N-ku
ke-ke-ko-ke-ka-ke-N-ke
ko-ko-ka-ko-ki-ko-N-ko
cha-cha-chi-cha-chu-cha-N-cha
chi-chi-chu-chi-che-chi-N-chi
chu-chu-che-chu-cho-chu-N-chu
che-che-cho-che-cha-che-N-che
cho-cho-cha-cho-chi-cho-N-cho

We know that seven samples will be pulled from every string and what their parameter values will be. We also know that the first alias will look like [- CV] and the other six will look like [V CV].

The C's are the same throughout every line, so we only need to index one of them. The vowels follow an alternating pattern, but besides the nasal there are only three unique vowels per line. We also need to index the entire string so that we can duplciate the filename for each line.

Applying this logic, let's look at what our substring indexation will look like before we start replacing characters in our Find expression:
Code:
^((k)(a)-ka-k(i)-ka-k(u)-ka-N-ka)$

The parentheses are read left-to-right, outside-in, so [$1] will store the entire string, [$2] is our consonant, and [$3 $4 $5] are our vowels. We do not need to index [n] for these strings because it is in the same position in every line.

Because the consonants and vowels vary for each line of the reclist, we can next replace them with wildcards [.]:
Code:
^((.)(.)-..-.(.)-..-.(.)-..-N-..)$

Now, our substring indexes are matching with any character that falls in that position of the string rather than a specific C or V. We can leave the consistent parts of the string intact, like the hyphens and nasal.

There is a minor issue here though; one of our consonants is represented with more than one character, which means this expression as-is will not match those strings. If we want to generate the whole list in one operation, we cannot simply match two wildcards like [(..)] because that would simply reverse the problem and no longer match the [k] strings.

Instead, we can use the wildcard along with the quantifier [*] for every consonant.
Code:
^((.*)(.)-.*.-.*(.)-.*.-.*(.)-.*.-N-.*.)$

This will match any number of characters as appearing before a single character and a hyphen, which in our case captures the variation in consonant length. This does, however, rely on vowels only ever being the same character length. If vowels have variable length and consonants are consistent, you can place the quantifier [*] after the second [.] instead, like [..*]. If both vowels and consonants have variable lengths, you will have to generate some strings separately.

The next step is to set up our Replace expression. You may find it easier to first write this out with linebreaks in a text editor before combining it into a single line in the find and replace menu.

Remember to begin every line with the filename, which is stored here as [$1], followed by [.wav=].

After this we can use the substring indexes for our consonant and vowels to build each alias. You may also find it useful to note down which substring index refers to which character(s) to help you remember, especially when working with very long or complex reclist segments.

Next, we'll type in every value for that line of the oto separated with commas, no different than if we were creating it completely by hand, and finally close it off with [\r]. We'll repeat this for every line of the oto until we get something like this:
Code:
$1.wav=- $2$3,875,400,-600,300,100\r
$1.wav=$3 $2$3,1375,400,-600,300,100\r
$1.wav=$3 $2$4,1875,400,-600,300,100\r
$1.wav=$4 $2$3,2375,400,-600,300,100\r
$1.wav=$3 $2$5,2875,400,-600,300,100\r
$1.wav=$5 $2$3,3375,400,-600,300,100\r
$1.wav=n $2$3,4375,400,-600,300,100

Here it is with no linebreaks, which is how it should actually be entered in the Replace field:
Code:
$1.wav=- $2$3,875,400,-600,300,100\r$1.wav=$3 $2$3,1375,400,-600,300,100\r$1.wav=$3 $2$4,1875,400,-600,300,100\r$1.wav=$4 $2$3,2375,400,-600,300,100\r$1.wav=$3 $2$5,2875,400,-600,300,100\r$1.wav=$5 $2$3,3375,400,-600,300,100\r$1.wav=n $2$3,4375,400,-600,300,100

Performing this operation yields us with:
Code:
ka-ka-ki-ka-ku-ka-N-ka.wav=- ka,875,400,-600,300,100
ka-ka-ki-ka-ku-ka-N-ka.wav=a ka,1375,400,-600,300,100
ka-ka-ki-ka-ku-ka-N-ka.wav=a ki,1875,400,-600,300,100
ka-ka-ki-ka-ku-ka-N-ka.wav=i ka,2375,400,-600,300,100
ka-ka-ki-ka-ku-ka-N-ka.wav=a ku,2875,400,-600,300,100
ka-ka-ki-ka-ku-ka-N-ka.wav=u ka,3375,400,-600,300,100
ka-ka-ki-ka-ku-ka-N-ka.wav=n ka,4375,400,-600,300,100
ki-ki-ku-ki-ke-ki-N-ki.wav=- ki,875,400,-600,300,100
ki-ki-ku-ki-ke-ki-N-ki.wav=i ki,1375,400,-600,300,100
ki-ki-ku-ki-ke-ki-N-ki.wav=i ku,1875,400,-600,300,100
ki-ki-ku-ki-ke-ki-N-ki.wav=u ki,2375,400,-600,300,100
ki-ki-ku-ki-ke-ki-N-ki.wav=i ke,2875,400,-600,300,100
ki-ki-ku-ki-ke-ki-N-ki.wav=e ki,3375,400,-600,300,100
ki-ki-ku-ki-ke-ki-N-ki.wav=n ki,4375,400,-600,300,100
ku-ku-ke-ku-ko-ku-N-ku.wav=- ku,875,400,-600,300,100
ku-ku-ke-ku-ko-ku-N-ku.wav=u ku,1375,400,-600,300,100
ku-ku-ke-ku-ko-ku-N-ku.wav=u ke,1875,400,-600,300,100
ku-ku-ke-ku-ko-ku-N-ku.wav=e ku,2375,400,-600,300,100
ku-ku-ke-ku-ko-ku-N-ku.wav=u ko,2875,400,-600,300,100
ku-ku-ke-ku-ko-ku-N-ku.wav=o ku,3375,400,-600,300,100
ku-ku-ke-ku-ko-ku-N-ku.wav=n ku,4375,400,-600,300,100
ke-ke-ko-ke-ka-ke-N-ke.wav=- ke,875,400,-600,300,100
ke-ke-ko-ke-ka-ke-N-ke.wav=e ke,1375,400,-600,300,100
ke-ke-ko-ke-ka-ke-N-ke.wav=e ko,1875,400,-600,300,100
ke-ke-ko-ke-ka-ke-N-ke.wav=o ke,2375,400,-600,300,100
ke-ke-ko-ke-ka-ke-N-ke.wav=e ka,2875,400,-600,300,100
ke-ke-ko-ke-ka-ke-N-ke.wav=a ke,3375,400,-600,300,100
ke-ke-ko-ke-ka-ke-N-ke.wav=n ke,4375,400,-600,300,100
ko-ko-ka-ko-ki-ko-N-ko.wav=- ko,875,400,-600,300,100
ko-ko-ka-ko-ki-ko-N-ko.wav=o ko,1375,400,-600,300,100
ko-ko-ka-ko-ki-ko-N-ko.wav=o ka,1875,400,-600,300,100
ko-ko-ka-ko-ki-ko-N-ko.wav=a ko,2375,400,-600,300,100
ko-ko-ka-ko-ki-ko-N-ko.wav=o ki,2875,400,-600,300,100
ko-ko-ka-ko-ki-ko-N-ko.wav=i ko,3375,400,-600,300,100
ko-ko-ka-ko-ki-ko-N-ko.wav=n ko,4375,400,-600,300,100
cha-cha-chi-cha-chu-cha-N-cha.wav=- cha,875,400,-600,300,100
cha-cha-chi-cha-chu-cha-N-cha.wav=a cha,1375,400,-600,300,100
cha-cha-chi-cha-chu-cha-N-cha.wav=a chi,1875,400,-600,300,100
cha-cha-chi-cha-chu-cha-N-cha.wav=i cha,2375,400,-600,300,100
cha-cha-chi-cha-chu-cha-N-cha.wav=a chu,2875,400,-600,300,100
cha-cha-chi-cha-chu-cha-N-cha.wav=u cha,3375,400,-600,300,100
cha-cha-chi-cha-chu-cha-N-cha.wav=n cha,4375,400,-600,300,100
chi-chi-chu-chi-che-chi-N-chi.wav=- chi,875,400,-600,300,100
chi-chi-chu-chi-che-chi-N-chi.wav=i chi,1375,400,-600,300,100
chi-chi-chu-chi-che-chi-N-chi.wav=i chu,1875,400,-600,300,100
chi-chi-chu-chi-che-chi-N-chi.wav=u chi,2375,400,-600,300,100
chi-chi-chu-chi-che-chi-N-chi.wav=i che,2875,400,-600,300,100
chi-chi-chu-chi-che-chi-N-chi.wav=e chi,3375,400,-600,300,100
chi-chi-chu-chi-che-chi-N-chi.wav=n chi,4375,400,-600,300,100
chu-chu-che-chu-cho-chu-N-chu.wav=- chu,875,400,-600,300,100
chu-chu-che-chu-cho-chu-N-chu.wav=u chu,1375,400,-600,300,100
chu-chu-che-chu-cho-chu-N-chu.wav=u che,1875,400,-600,300,100
chu-chu-che-chu-cho-chu-N-chu.wav=e chu,2375,400,-600,300,100
chu-chu-che-chu-cho-chu-N-chu.wav=u cho,2875,400,-600,300,100
chu-chu-che-chu-cho-chu-N-chu.wav=o chu,3375,400,-600,300,100
chu-chu-che-chu-cho-chu-N-chu.wav=n chu,4375,400,-600,300,100
che-che-cho-che-cha-che-N-che.wav=- che,875,400,-600,300,100
che-che-cho-che-cha-che-N-che.wav=e che,1375,400,-600,300,100
che-che-cho-che-cha-che-N-che.wav=e cho,1875,400,-600,300,100
che-che-cho-che-cha-che-N-che.wav=o che,2375,400,-600,300,100
che-che-cho-che-cha-che-N-che.wav=e cha,2875,400,-600,300,100
che-che-cho-che-cha-che-N-che.wav=a che,3375,400,-600,300,100
che-che-cho-che-cha-che-N-che.wav=n che,4375,400,-600,300,100
cho-cho-cha-cho-chi-cho-N-cho.wav=- cho,875,400,-600,300,100
cho-cho-cha-cho-chi-cho-N-cho.wav=o cho,1375,400,-600,300,100
cho-cho-cha-cho-chi-cho-N-cho.wav=o cha,1875,400,-600,300,100
cho-cho-cha-cho-chi-cho-N-cho.wav=a cho,2375,400,-600,300,100
cho-cho-cha-cho-chi-cho-N-cho.wav=o chi,2875,400,-600,300,100
cho-cho-cha-cho-chi-cho-N-cho.wav=i cho,3375,400,-600,300,100
cho-cho-cha-cho-chi-cho-N-cho.wav=n cho,4375,400,-600,300,100

Thus, with one single operation, we have generated the vast majority of the base oto — no copypasting required.

For another example, let's look at the 2-mora CVVC strings from earlier.
Code:
ka-kak
ki-kik
ku-kuk
ke-kek
ko-kok
N-ka
cha-chach
chi-chich
chu-chuch
che-chech
cho-choch
N-chi

For these, it will likely be easiest to generate the [N] strings separately rather than trying to do the whole thing in one operation because they are structured quite differently.

To match the majority of the strings, we can highlight every string except for the [N] strings and Find this expression:
Code:
^((.*)(.)-.*..*)$

And Replace with:
Code:
$1.wav=- $2$3,975,300,-500,200,50\r$1.wav=$3 $2,1350,225,-250,200,50\r$1.wav=$2$3,1550,250,-400,100,25\r$1.wav=$3 $2-,1975,300,-500,200,50

For the [N] strings, we can Find this expression:
Code:
^(N-(.*)..*)$

And Replace with:
Code:
$1.wav=n $2,1350,225,-250,200,50

And here's our final result:
Code:
ka-kak.wav=- ka,975,300,-500,200,50
ka-kak.wav=a k,1350,225,-250,200,50
ka-kak.wav=ka,1550,250,-400,100,25
ka-kak.wav=a k-,1975,300,-500,200,50
ki-kik.wav=- ki,975,300,-500,200,50
ki-kik.wav=i k,1350,225,-250,200,50
ki-kik.wav=ki,1550,250,-400,100,25
ki-kik.wav=i k-,1975,300,-500,200,50
ku-kuk.wav=- ku,975,300,-500,200,50
ku-kuk.wav=u k,1350,225,-250,200,50
ku-kuk.wav=ku,1550,250,-400,100,25
ku-kuk.wav=u k-,1975,300,-500,200,50
ke-kek.wav=- ke,975,300,-500,200,50
ke-kek.wav=e k,1350,225,-250,200,50
ke-kek.wav=ke,1550,250,-400,100,25
ke-kek.wav=e k-,1975,300,-500,200,50
ko-kok.wav=- ko,975,300,-500,200,50
ko-kok.wav=o k,1350,225,-250,200,50
ko-kok.wav=ko,1550,250,-400,100,25
ko-kok.wav=o k-,1975,300,-500,200,50
N-ka.wav=n k,1350,225,-250,200,50
cha-chach.wav=- cha,975,300,-500,200,50
cha-chach.wav=a ch,1350,225,-250,200,50
cha-chach.wav=cha,1550,250,-400,100,25
cha-chach.wav=a ch-,1975,300,-500,200,50
chi-chich.wav=- chi,975,300,-500,200,50
chi-chich.wav=i ch,1350,225,-250,200,50
chi-chich.wav=chi,1550,250,-400,100,25
chi-chich.wav=i ch-,1975,300,-500,200,50
chu-chuch.wav=- chu,975,300,-500,200,50
chu-chuch.wav=u ch,1350,225,-250,200,50
chu-chuch.wav=chu,1550,250,-400,100,25
chu-chuch.wav=u ch-,1975,300,-500,200,50
che-chech.wav=- che,975,300,-500,200,50
che-chech.wav=e ch,1350,225,-250,200,50
che-chech.wav=che,1550,250,-400,100,25
che-chech.wav=e ch-,1975,300,-500,200,50
cho-choch.wav=- cho,975,300,-500,200,50
cho-choch.wav=o ch,1350,225,-250,200,50
cho-choch.wav=cho,1550,250,-400,100,25
cho-choch.wav=o ch-,1975,300,-500,200,50
N-chi.wav=n ch,1350,225,-250,200,50
  • Like
Reactions: Kiyoteru
Author
FelineWasteland
Views
225
First release
Last update
Rating
0.00 star(s) 0 ratings

More resources from FelineWasteland