Task 755: .UST File Format

Task 755: .UST File Format

1. List of All Properties of the .UST File Format Intrinsic to Its File System

The .UST file format is a plain text-based format used by the UTAU vocal synthesis software. It is structured as an INI-like file with sections denoted by square brackets (e.g., [#SETTING]). The file begins with a version section, followed by settings, a series of numbered note sections, and ends with a track end marker. There is no binary header or low-level file system metadata specific to .UST beyond standard text file attributes (e.g., UTF-8 or Shift-JIS encoding, line endings). The "properties" refer to the key-value pairs within the file's sections, which define the sequence data for vocal synthesis. Based on the format specifications, the complete list of properties is as follows:

Version Section ([#VERSION])

  • Version: Specifies the format version (e.g., "UST1.2" or "UST2.0").

Settings Section ([#SETTING])

  • ProjectName: The name of the project (string).
  • OutFile: The path to the output WAV file (string).
  • VoiceDir: The directory path to the voicebank (string).
  • CacheDir: The directory path for cache files (string).
  • Tool1: The filename of the wavtool executable (string, e.g., "wavtool.exe").
  • Tool2: The filename of the resampler executable (string, e.g., "resampler.exe").
  • Tempo: The tempo in beats per minute (float, e.g., 120.00).
  • Tracks: The number of tracks (integer, typically 1).
  • Mode2: Enables advanced mode for per-note tempo changes (boolean, "True" or "False").
  • Flags: Global flags for rendering (string).
  • RenderOptions: Additional rendering parameters (string, optional).

Note Sections ([#0000], [#0001], etc.)

Each note section has a unique number and contains the following properties:

  • Length: The duration of the note in ticks (integer, e.g., 480 for a quarter note at 120 BPM).
  • Lyric: The lyric or phoneme to sing (string, e.g., "あ" or "R" for rest).
  • NoteNum: The MIDI note number (integer, e.g., 60 for middle C).
  • Intensity: The volume intensity (integer, 0-100).
  • Modulation: The depth of pitch modulation (integer, 0-100).
  • PreUtterance: The pre-utterance time in milliseconds (float).
  • VoiceOverlap: The overlap time in milliseconds (float).
  • Velocity: The consonant velocity (float, 0-200).
  • Flags: Note-specific flags for rendering (string, e.g., "g-5").
  • STP: The start point offset in milliseconds (float).
  • Envelope: The envelope parameters as a comma-separated string (e.g., "0,5,35,0,100,100,100,100,100,100,10").
  • PBType: The pitch bend type (integer, e.g., 5 for s-curve).
  • Pitches: The pitch bend values as a comma-separated string of numbers.
  • PBStart: The starting point for pitch bends (float).
  • PBW: Pitch bend widths (comma-separated string).
  • PBY: Pitch bend Y offsets (comma-separated string).
  • PBM: Pitch bend modes (string).
  • VBR: Vibrato parameters as a comma-separated string (e.g., "100,64,30,50,50,50,50,0").
  • Label: A custom label for the note (string, optional).
  • $direct: Enables direct rendering (boolean, "True" or "False").
  • $patch: Specifies a patch or override (string, optional).
  • Tempo: Per-note tempo if Mode2 is enabled (float, optional).

End Section ([#TRACKEND])

  • No properties; marks the end of the file.

These properties are intrinsic to the .UST format and are parsed sequentially from the text file.

The following are direct download links to .UST files from public sources. These links initiate immediate downloads of individual .UST files (or archives containing them, as single-file direct .UST downloads are rare; the files within are .UST format):

3. Ghost Blog Embedded HTML JavaScript for Drag and Drop .UST File Dump

The following is a self-contained HTML document with embedded JavaScript that can be embedded in a Ghost blog (or any HTML-enabled blog platform). It allows users to drag and drop a .UST file, parses it, and dumps all properties from the settings and note sections to the screen.

.UST File Property Dumper
Drag and drop a .UST file here

4. Python Class for .UST File Handling

The following Python class can open, decode (parse), read, write, and print all properties from a .UST file.

import os

class USTFile:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {'settings': {}, 'notes': []}
        self.read()

    def read(self):
        if not os.path.exists(self.filepath):
            raise FileNotFoundError(f"File {self.filepath} not found.")
        with open(self.filepath, 'r', encoding='utf-8') as f:
            content = f.read()
        lines = content.splitlines()
        current_section = None
        current_note = None
        for line in lines:
            line = line.strip()
            if line.startswith('[#VERSION]'):
                current_section = 'version'
            elif line.startswith('[#SETTING]'):
                current_section = 'settings'
            elif line.startswith('[#') and line.endswith(']'):
                if current_note:
                    self.properties['notes'].append(current_note)
                current_note = {'section': line}
                current_section = 'note'
            elif line.startswith('[#TRACKEND]'):
                if current_note:
                    self.properties['notes'].append(current_note)
                current_section = None
            elif '=' in line and current_section:
                key, value = line.split('=', 1)
                if current_section == 'settings':
                    self.properties['settings'][key] = value
                elif current_section == 'note':
                    current_note[key] = value

    def print_properties(self):
        print("Settings:")
        for key, value in self.properties['settings'].items():
            print(f"{key}: {value}")
        print("\nNotes:")
        for note in self.properties['notes']:
            print(f"Section: {note['section']}")
            for key, value in note.items():
                if key != 'section':
                    print(f"  {key}: {value}")
            print("---")

    def write(self, new_filepath=None):
        if new_filepath is None:
            new_filepath = self.filepath
        with open(new_filepath, 'w', encoding='utf-8') as f:
            f.write('[#VERSION]\nUST1.2\n')  # Default version
            f.write('[#SETTING]\n')
            for key, value in self.properties['settings'].items():
                f.write(f"{key}={value}\n")
            for note in self.properties['notes']:
                f.write(f"{note['section']}\n")
                for key, value in note.items():
                    if key != 'section':
                        f.write(f"{key}={value}\n")
            f.write('[#TRACKEND]\n')

# Example usage:
# ust = USTFile('example.ust')
# ust.print_properties()
# ust.write('new.ust')

5. Java Class for .UST File Handling

The following Java class can open, decode (parse), read, write, and print all properties from a .UST file.

import java.io.*;
import java.util.*;

public class USTFile {
    private String filepath;
    private Map<String, String> settings = new LinkedHashMap<>();
    private List<Map<String, String>> notes = new ArrayList<>();

    public USTFile(String filepath) {
        this.filepath = filepath;
        read();
    }

    private void read() {
        try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) {
            String line;
            String currentSection = null;
            Map<String, String> currentNote = null;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.startsWith("[#VERSION]")) {
                    currentSection = "version";
                } else if (line.startsWith("[#SETTING]")) {
                    currentSection = "settings";
                } else if (line.startsWith("[#") && line.endsWith("]")) {
                    if (currentNote != null) {
                        notes.add(currentNote);
                    }
                    currentNote = new LinkedHashMap<>();
                    currentNote.put("section", line);
                    currentSection = "note";
                } else if (line.startsWith("[#TRACKEND]")) {
                    if (currentNote != null) {
                        notes.add(currentNote);
                    }
                    currentSection = null;
                } else if (line.contains("=") && currentSection != null) {
                    String[] parts = line.split("=", 2);
                    String key = parts[0];
                    String value = parts.length > 1 ? parts[1] : "";
                    if ("settings".equals(currentSection)) {
                        settings.put(key, value);
                    } else if ("note".equals(currentSection)) {
                        currentNote.put(key, value);
                    }
                }
            }
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
        }
    }

    public void printProperties() {
        System.out.println("Settings:");
        for (Map.Entry<String, String> entry : settings.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        System.out.println("\nNotes:");
        for (Map<String, String> note : notes) {
            System.out.println("Section: " + note.get("section"));
            for (Map.Entry<String, String> entry : note.entrySet()) {
                if (!"section".equals(entry.getKey())) {
                    System.out.println("  " + entry.getKey() + ": " + entry.getValue());
                }
            }
            System.out.println("---");
        }
    }

    public void write(String newFilepath) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(newFilepath == null ? filepath : newFilepath))) {
            writer.write("[#VERSION]\nUST1.2\n");
            writer.write("[#SETTING]\n");
            for (Map.Entry<String, String> entry : settings.entrySet()) {
                writer.write(entry.getKey() + "=" + entry.getValue() + "\n");
            }
            for (Map<String, String> note : notes) {
                writer.write(note.get("section") + "\n");
                for (Map.Entry<String, String> entry : note.entrySet()) {
                    if (!"section".equals(entry.getKey())) {
                        writer.write(entry.getKey() + "=" + entry.getValue() + "\n");
                    }
                }
            }
            writer.write("[#TRACKEND]\n");
        } catch (IOException e) {
            System.err.println("Error writing file: " + e.getMessage());
        }
    }

    // Example usage:
    // public static void main(String[] args) {
    //     USTFile ust = new USTFile("example.ust");
    //     ust.printProperties();
    //     ust.write("new.ust");
    // }
}

6. JavaScript Class for .UST File Handling

The following JavaScript class can open (using FileReader for browser), decode (parse), read, write (using Blob for download), and print all properties from a .UST file to the console.

class USTFile {
    constructor(file) {
        this.file = file;
        this.properties = { settings: {}, notes: [] };
    }

    async read() {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = (event) => {
                const content = event.target.result;
                const lines = content.split(/\r?\n/);
                let currentSection = null;
                let currentNote = null;
                lines.forEach(line => {
                    line = line.trim();
                    if (line.startsWith('[#VERSION]')) {
                        currentSection = 'version';
                    } else if (line.startsWith('[#SETTING]')) {
                        currentSection = 'settings';
                    } else if (line.startsWith('[#') && line.endsWith(']')) {
                        if (currentNote) this.properties.notes.push(currentNote);
                        currentNote = { section: line };
                        currentSection = 'note';
                    } else if (line.startsWith('[#TRACKEND]')) {
                        if (currentNote) this.properties.notes.push(currentNote);
                        currentSection = null;
                    } else if (line.includes('=') && currentSection) {
                        const [key, value] = line.split('=');
                        if (currentSection === 'settings') {
                            this.properties.settings[key] = value;
                        } else if (currentSection === 'note') {
                            currentNote[key] = value;
                        }
                    }
                });
                resolve();
            };
            reader.onerror = reject;
            reader.readAsText(this.file);
        });
    }

    printProperties() {
        console.log('Settings:');
        for (const [key, value] of Object.entries(this.properties.settings)) {
            console.log(`${key}: ${value}`);
        }
        console.log('\nNotes:');
        this.properties.notes.forEach(note => {
            console.log(`Section: ${note.section}`);
            for (const [key, value] of Object.entries(note)) {
                if (key !== 'section') {
                    console.log(`  ${key}: ${value}`);
                }
            }
            console.log('---');
        });
    }

    write(filename = 'new.ust') {
        let content = '[#VERSION]\nUST1.2\n[#SETTING]\n';
        for (const [key, value] of Object.entries(this.properties.settings)) {
            content += `${key}=${value}\n`;
        }
        this.properties.notes.forEach(note => {
            content += `${note.section}\n`;
            for (const [key, value] of Object.entries(note)) {
                if (key !== 'section') {
                    content += `${key}=${value}\n`;
                }
            }
        });
        content += '[#TRACKEND]\n';
        const blob = new Blob([content], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        a.click();
        URL.revokeObjectURL(url);
    }
}

// Example usage (in browser):
// const input = document.createElement('input');
// input.type = 'file';
// input.onchange = async (e) => {
//     const ust = new USTFile(e.target.files[0]);
//     await ust.read();
//     ust.printProperties();
//     ust.write();
// };
// document.body.appendChild(input);

7. C Class for .UST File Handling

The following is a C++ class (as "c class" likely refers to C++ for class support) that can open, decode (parse), read, write, and print all properties from a .UST file to the console.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <sstream>

class USTFile {
private:
    std::string filepath;
    std::map<std::string, std::string> settings;
    std::vector<std::map<std::string, std::string>> notes;

public:
    USTFile(const std::string& fp) : filepath(fp) {
        read();
    }

    void read() {
        std::ifstream file(filepath);
        if (!file.is_open()) {
            std::cerr << "File not found: " << filepath << std::endl;
            return;
        }
        std::string line;
        std::string currentSection;
        std::map<std::string, std::string> currentNote;
        while (std::getline(file, line)) {
            line.erase(0, line.find_first_not_of(" \t"));
            line.erase(line.find_last_not_of(" \t") + 1);
            if (line == "[#VERSION]") {
                currentSection = "version";
            } else if (line == "[#SETTING]") {
                currentSection = "settings";
            } else if (line.rfind("[#", 0) == 0 && line.back() == ']') {
                if (!currentNote.empty()) {
                    notes.push_back(currentNote);
                    currentNote.clear();
                }
                currentNote["section"] = line;
                currentSection = "note";
            } else if (line == "[#TRACKEND]") {
                if (!currentNote.empty()) {
                    notes.push_back(currentNote);
                }
                currentSection = "";
            } else if (line.find('=') != std::string::npos && !currentSection.empty()) {
                std::istringstream iss(line);
                std::string key, value;
                std::getline(iss, key, '=');
                std::getline(iss, value);
                if (currentSection == "settings") {
                    settings[key] = value;
                } else if (currentSection == "note") {
                    currentNote[key] = value;
                }
            }
        }
        file.close();
    }

    void printProperties() const {
        std::cout << "Settings:" << std::endl;
        for (const auto& pair : settings) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
        std::cout << "\nNotes:" << std::endl;
        for (const auto& note : notes) {
            std::cout << "Section: " << note.at("section") << std::endl;
            for (const auto& pair : note) {
                if (pair.first != "section") {
                    std::cout << "  " << pair.first << ": " << pair.second << std::endl;
                }
            }
            std::cout << "---" << std::endl;
        }
    }

    void write(const std::string& newFilepath = "") const {
        std::string outPath = newFilepath.empty() ? filepath : newFilepath;
        std::ofstream file(outPath);
        if (!file.is_open()) {
            std::cerr << "Error writing to file: " << outPath << std::endl;
            return;
        }
        file << "[#VERSION]" << std::endl << "UST1.2" << std::endl;
        file << "[#SETTING]" << std::endl;
        for (const auto& pair : settings) {
            file << pair.first << "=" << pair.second << std::endl;
        }
        for (const auto& note : notes) {
            file << note.at("section") << std::endl;
            for (const auto& pair : note) {
                if (pair.first != "section") {
                    file << pair.first << "=" << pair.second << std::endl;
                }
            }
        }
        file << "[#TRACKEND]" << std::endl;
        file.close();
    }
};

// Example usage:
// int main() {
//     USTFile ust("example.ust");
//     ust.printProperties();
//     ust.write("new.ust");
//     return 0;
// }