├── .github └── workflows │ └── build.yml ├── LICENSE ├── README.MD ├── TextGrid2otoWEBUI.bat ├── WEB-UI.py ├── app.py ├── config ├── run-config-CV.txt ├── run-config-CVR.txt ├── run-config-CVVC.txt └── run-config-VCV.txt ├── dictionary ├── en.txt ├── hira2roma_list.txt ├── ja.txt └── zh.txt ├── hira2roma.py ├── img └── TextGrid2oto.ico ├── json2oto ├── json2CV_oto.py ├── json2VCV_oto.py └── json2oto.py ├── main.py ├── oto ├── oto_check.py └── oto_rw.py ├── presamp ├── CVR中文-presamp.ini ├── arpasing-presamp.ini ├── jp-hira-presamp.ini ├── jp-roma-hira-presamp.ini ├── jp-romaji-presamp.ini ├── risku中文CVVCpresamp.ini └── 樗儿式中文VCV-presamp.ini ├── requirements.txt ├── textgrid2json ├── TextGrid2ds_json.py ├── del_SP.py ├── ds_json2filter.py ├── ds_json2word.py ├── transcriptions_make.py └── word2utau_phone.py ├── wavname2lab.py └── 音频拼接并生成lab.py /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: 自动打包发布程序 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' # 添加标签推送触发 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | description: '输入自定义版本号 (例如: 1.2.3)' 11 | required: false 12 | default: '0.1.2' 13 | 14 | jobs: 15 | build: 16 | runs-on: windows-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | 22 | - name: 安装requirements 23 | run: pip install nuitka 24 | 25 | - name: 并行打包 26 | run: | 27 | # 启动两个后台任务进行打包 28 | # --onefile 29 | Start-Job -ScriptBlock { nuitka --standalone --windows-icon-from-ico=img/TextGrid2oto.ico --output-filename=TextGrid2oto --assume-yes-for-downloads --output-dir=output main.py } 30 | Start-Job -ScriptBlock { nuitka --standalone --assume-yes-for-downloads --output-dir=output hira2roma.py } 31 | # 等待所有任务完成 32 | Get-Job | Wait-Job 33 | 34 | - name: 创建发布压缩包 35 | id: create_release # 添加步骤ID用于后续引用 36 | shell: pwsh 37 | run: | 38 | # 处理版本号逻辑 39 | if ("${{ github.event.inputs.version }}" -ne "") { 40 | $version = "${{ github.event.inputs.version }}" 41 | } elseif ("${{ github.ref_type }}" -eq "tag") { 42 | $version = "${{ github.ref_name }}".Replace("v", "") 43 | } else { 44 | $version = "Test" 45 | } 46 | # 通过 GitHub API 获取最新标签 47 | $headers = @{ 48 | "Authorization" = "Bearer ${{ secrets.GITHUB_TOKEN }}" 49 | } 50 | $tagsUrl = "https://api.github.com/repos/xiaobaijunya/TextGrid2oto/tags" 51 | $tagsResponse = Invoke-RestMethod -Uri $tagsUrl -Headers $headers 52 | $latestTag = $tagsResponse[0].name -replace 'v', '' 53 | 54 | # 设置输出变量 55 | echo "version=$version" >> $env:GITHUB_OUTPUT 56 | # 删除所有py文件 57 | Get-ChildItem -Path D:\a\TextGrid2oto\TextGrid2oto\ -Filter *.py -Recurse | Remove-Item -Force 58 | 59 | # 删除空文件夹 60 | Get-ChildItem -Path D:\a\TextGrid2oto\TextGrid2oto\ -Directory -Recurse | Where-Object { (Get-ChildItem -Path $_.FullName | Measure-Object).Count -eq 0 } | Remove-Item -Force -Recurse 61 | 62 | # 把output文件里面的dll和exe复制到主目录 63 | $sourceDir = "D:\a\TextGrid2oto\TextGrid2oto\output" 64 | $destinationDir = "D:\a\TextGrid2oto\TextGrid2oto" 65 | Get-ChildItem -Path $sourceDir -Filter *.dll -Recurse | Copy-Item -Destination $destinationDir 66 | Get-ChildItem -Path $sourceDir -Filter *.exe -Recurse | Copy-Item -Destination $destinationDir 67 | 68 | # 删除output文件夹 69 | Remove-Item -Path $sourceDir -Force -Recurse 70 | 71 | # 删除 img、.idea 和 .github 文件夹 72 | $foldersToDelete = "D:\a\TextGrid2oto\TextGrid2oto\img", "D:\a\TextGrid2oto\TextGrid2oto\.idea", "D:\a\TextGrid2oto\TextGrid2oto\.github", "D:\a\TextGrid2oto\TextGrid2oto\requirements.txt","D:\a\TextGrid2oto\TextGrid2oto\.gitignore" 73 | foreach ($folder in $foldersToDelete) { 74 | if (Test-Path -Path $folder) { 75 | Remove-Item -Path $folder -Force -Recurse 76 | } 77 | } 78 | 79 | # 压缩主目录 80 | Compress-Archive -Path D:\a\TextGrid2oto\TextGrid2oto\* -DestinationPath D:\a\TextGrid2oto\TextGrid2oto\TextGrid2oto-$version-windows.zip 81 | ls D:\a\TextGrid2oto\TextGrid2oto\ 82 | 83 | 84 | - name: 发布到 GitHub Releases 85 | uses: softprops/action-gh-release@v1 86 | with: 87 | tag_name: ${{ github.ref_name }} 88 | name: "Release ${{ steps.create_release.outputs.version }}" # 使用上一步的输出变量 89 | draft: false 90 | prerelease: false 91 | files: | 92 | TextGrid2oto-*-windows.zip 93 | body: | 94 | ## 版本 ${{ steps.create_release.outputs.version }} 更新内容 95 | 查看 [更新细节](https://github.com/xiaobaijunya/TextGrid2oto/compare/v${{ steps.create_release.outputs.latestTag }}...v${{ steps.create_release.outputs.version }}) 96 | 97 | ### 包含组件 98 | - TextGrid2oto (Windows x64) 99 | - hira2roma (Windows x64) 100 | ### 说明文件 101 | - README.md 102 | 103 | env: 104 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # **TextGrid2oto** 2 | 3 | 这是一个把TextGrid转换为oto的程序,本程序*现**已支持多段标注模型***。请您在使用前先了解utau的**presamp.ini**字典以及**SOFA模型字典**,目前暂时**不支持多语种标记**的转换,但您可以通过修改多语种字典为您需要的单语种,进行使用。 4 | 5 | ## 现已支持 ***CVVC,VCV,CVVR,扩张整音*** 四种utau标记类型 6 | 7 | ## *现已支持多段标注模型* 8 | 9 | ## 使用此程序,你需要准备: 10 | 11 | 1. [SOFA](https://github.com/qiuqiao/SOFA):TextGrid标记生成 12 | 2. [qiuqiao/SOFA · Discussions · GitHub](https://github.com/qiuqiao/SOFA/discussions):在SOFA社区选择一个您的语言的SOFA模型 13 | 3. SOFA模型自带的字典(一般在模型文件里面) 14 | 4. [vlabeler](https://github.com/sdercolin/vlabeler):TextGrid标记修改软件([下载vlabeler的textgrid插件](https://github.com/sdercolin/vlabeler-textgrid)) 15 | 5. utau音制工具:setParam 或者 [vlabeler](https://github.com/sdercolin/vlabeler)的utau标注模式(推荐) 16 | 6. utau录音完成的wav文件(请先了解utau声库如何制作) 17 | 7. utau录音表配套的:presamp.ini(**现已支持CVVC和VCV**,请参考presamp文件夹内的文件) 18 | 8. 一台**Windows10及以上版本**的电脑(好像python自3.8.10后就不再支持win7了,很抱歉) 19 | 20 | ## 使用方法: 21 | 22 | 1. 在config文件夹里面选择一个音源类型的配置 23 | 2. 修改config文件里面必改的前三项 24 | 3. 将config.txt拖到程序时上 25 | 4. 程序运行生成lab文件 26 | 5. 前往sofa,选择对应语言的模型,生成**textgrid标记** 27 | 6. **(可选)**中文两段式模型使用sofa-dic文件夹中的SOFA-UTAUCHN-dic.txt,专门为utau声库制作的字典(**注意:部分模型不一定支持此字典中的音素**) 28 | 7. 返回程序,按Enter键继续生成 29 | 8. **(可选)**检查程序提示的缺少的音素 30 | 9. **(可选)**查找原因,反馈给开发者 31 | 10. oto生成完毕 32 | 33 | ## ***注意!!*** 34 | 35 | 1. 生成lab的时候,请确保您的wav名称的拼音或者假名 和实际音频内容可以一一对应 36 | 2. 你的模型字典中的 单词 要和 presamp 可以对应,如果 单词 在模型字典或者presamp 中不存在,则会被sofa和该程序删除,会导致您的部分的oto和实际音素有差别 37 | 3. 您可以直接修改sofa字典中的单词部分,但不要修改音素名 38 | 4. 您不必让 模型字典 的 音素名 和您的 presamp 的音素名完全一致,您只需要保持 单词 的一致性即可 39 | 5. 程序完全不读取TextGrid的words层,所以如果您需要修改TextGrid标记只需修改phones层 40 | 6. 您可以修改cv_sum和vc_sum,来改变CV和VC每一条线的占比,不建议修改左线和预发声这种位置比较固定的线的数值。。。 41 | 7. 您可以修改cv_offset和vc_offset来调整oto的偏移,可以实现一些例如辅音长度缩短和CVR音源的连贯性优化 42 | 8. 如果缺少CV部分,可以检查sofa模型字典的 单词 是否与presamp里面的不同,可以修改sofa字典的拼音列,让它符合presamp 43 | 44 | ## uatu采样征集 45 | 46 | 提交采样,助力我们训练全新的专用于拼接声库的标注模型 47 | 48 | huggingface(需要梯子):https://baibaibai-utau-vocalbank-collect.hf.space/ 49 | 50 | 阿里魔塔社区(直连):https://modelscope.cn/studios/xiaobaijunya/utau_Vocalbank_collect 51 | 52 | ## 模型推荐: 53 | 54 | 中文两段式模型:[SOFA_Models](https://github.com/BaiShuoQwQ/SOFA_Models):【支持opencpop-extension字典的两段式模型】 55 | 56 | 日语模型:[ Japanese Quadrilx SOFA model ](https://github.com/Muzium/muzium.github.io/releases/tag/v1.0.2)【支持假名和罗马音音源】 57 | 58 | ## 联系我们: 59 | 60 | QQ群聊:1036935644 61 | 62 | ### 未来更新计划。。。 63 | 64 | 1.全新的专用标注模型 65 | 66 | 2.给重构过的CVVC和CVR生成,重新调优数值 67 | 68 | 3.重构webUI结构 69 | -------------------------------------------------------------------------------- /TextGrid2otoWEBUI.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | chcp 65001 >nul 3 | 4 | set PYTHON_REL_PATH=workenv\python.exe 5 | set SCRIPT_REL_PATH=WEB-UI.py 6 | set REQUIREMENTS_FILE=requirements.txt 7 | 8 | set SCRIPT_DIR=%~dp0 9 | set PYTHON_PATH=%SCRIPT_DIR%%PYTHON_REL_PATH% 10 | set SCRIPT_PATH=%SCRIPT_DIR%%SCRIPT_REL_PATH% 11 | 12 | echo 启动 TextGrid2oto-WEBUI 13 | "%PYTHON_PATH%" "%SCRIPT_PATH%" 14 | pause 15 | -------------------------------------------------------------------------------- /WEB-UI.py: -------------------------------------------------------------------------------- 1 | import webview 2 | import app 3 | import threading 4 | import queue 5 | # nuitka --standalone --windows-icon-from-ico=img/TextGrid2oto.ico --output-dir=output --output-filename=TextGrid2oto-WEBUI WEB-UI.py 6 | 7 | def run_app(): 8 | app.run() 9 | 10 | # 创建一个线程来运行 app 11 | app_thread = threading.Thread(target=run_app) 12 | app_thread.daemon = True # 将线程设置为守护线程,这样主线程退出时,子线程也会退出 13 | app_thread.start() 14 | w=960*1.2 15 | h=640*1.2 16 | print("启动 PyWebview 窗口...") 17 | window = webview.create_window( 18 | title='TextGrid2oto', 19 | url='http://127.0.0.1:7860', 20 | width=int(w), 21 | height=int(h), 22 | ) 23 | 24 | webview.start() 25 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | from tkinter import filedialog 3 | import wavname2lab 4 | from textgrid2json import ds_json2filter, word2utau_phone, TextGrid2ds_json, ds_json2word,transcriptions_make,del_SP 5 | from json2oto import json2CV_oto, json2oto, json2VCV_oto 6 | from oto import oto_check 7 | from oto import oto_rw 8 | import sys 9 | import os 10 | import subprocess 11 | import click 12 | 13 | def run(): 14 | demo.launch(server_port=7860, show_error=True, inbrowser=False) 15 | 16 | def generate_config( 17 | wav_path, ds_dict, presamp, cut, ignore, 18 | VCV_mode, lab, cv_sum, vc_sum, vv_sum, 19 | cv_offset, vc_offset, pitch, CV_repeat, 20 | VC_repeat, cover, sofa_model,SOFA_mode,SOFA_type,progress=gr.Progress()): 21 | config1 = ( 22 | f"wav_path={wav_path}\n" 23 | f"ds_dict={ds_dict}\n" 24 | f"presamp={presamp}\n" 25 | f"cut={cut}\n" 26 | f"ignore={ignore}\n" 27 | f"VCV_mode={VCV_mode}\n" 28 | f"lab={lab}\n" 29 | f"cv_sum={cv_sum}\n" 30 | f"vc_sum={vc_sum}\n" 31 | f"vv_sum={vv_sum}\n" 32 | f"cv_offset={cv_offset}\n" 33 | f"vc_offset={vc_offset}\n" 34 | f"pitch={pitch}\n" 35 | f"CV_repeat={CV_repeat}\n" 36 | f"VC_repeat={VC_repeat}\n" 37 | f"cover={cover}\n" 38 | f"sofa_model={sofa_model}\n" 39 | f"SOFA_mode={SOFA_mode}\n" 40 | f"SOFA_type={SOFA_type}\n" 41 | ) 42 | with open('config.txt', 'w', encoding='utf-8') as f: 43 | f.write(config1) 44 | if SOFA_type == 0: 45 | f.write(f'#python infer.py --folder {wav_path} --dictionary {os.path.abspath(ds_dict)} --ckpt {os.path.abspath(sofa_model)} --out_formats textgrid --save_confidence') 46 | elif SOFA_type == 1: 47 | f.write(f'#python infer.py --ckpt {os.path.abspath(sofa_model)} --folder {wav_path} --language {ds_dict.split('\\')[-1].split('/')[-1].split('.')[0]} --dictionary {os.path.abspath(ds_dict)} --save_confidence') 48 | progress(0, desc="✅ 配置文件已生成,开始执行主程序...") 49 | with open('config.txt', 'r', encoding='utf-8') as f: 50 | config = f.read().split('\n') 51 | for i in range(len(config)): 52 | config[i] = config[i].strip() 53 | # 转为字典 54 | config = {config[i].split('=')[0]: config[i].split('=')[1] for i in range(len(config)) if 55 | config[i] != '' and not config[i].startswith('#')} # 修改判断条件为跳过以#开头的行 56 | # 将字符串转换为列表并将每个元素转换为float类型 57 | config['cut'] = config['cut'].split(',') 58 | config['cv_sum'] = [float(i) for i in config['cv_sum'].strip('[]').split(',')] 59 | config['vc_sum'] = [float(i) for i in config['vc_sum'].strip('[]').split(',')] 60 | config['vv_sum'] = [float(i) for i in config['vv_sum'].strip('[]').split(',')] 61 | config['cv_offset'] = [float(i) for i in config['cv_offset'].strip('[]').split(',')] 62 | config['vc_offset'] = [float(i) for i in config['vc_offset'].strip('[]').split(',')] 63 | config['TextGrid_path'] = config['wav_path'] + '/TextGrid' 64 | progress(0.1,'1.配置文件读取成功') 65 | if config['lab'] == 'Y' or config['lab'] == 'y': 66 | progress(0.2,'1.生成lab') 67 | wavname2lab.run(config['wav_path'], config['cut']) 68 | transcriptions_make.create_transcriptions_csv(config['wav_path'], config['ds_dict']) 69 | if SOFA_mode == 0: 70 | if SOFA_type == 0: 71 | progress(0.3, '2.正在前往sofa生成TextGrid') 72 | sys.path.append('SOFA') 73 | from SOFA import infer 74 | print(f'--folder {wav_path} --dictionary {os.path.abspath(ds_dict)} --ckpt {os.path.abspath(sofa_model)} --out_formats textgrid --save_confidence') 75 | sys.argv = f'--folder {wav_path} --dictionary {os.path.abspath(ds_dict)} --ckpt {os.path.abspath(sofa_model)} --out_formats textgrid --save_confidence' 76 | # infer.main(ckpt=os.path.abspath(sofa_model),mode='force',ap_detector='LoudnessSpectralcentroidAPDetector',g2p=os.path.abspath(ds_dict),folder=wav_path,out_formats='textgrid',in_format='lab',save_confidence=True) 77 | with click.Context(infer.main) as ctx: 78 | result = ctx.invoke( 79 | infer.main, 80 | ckpt=os.path.abspath(sofa_model), 81 | folder=wav_path, 82 | dictionary=os.path.abspath(ds_dict), 83 | out_formats='textgrid', 84 | save_confidence=True 85 | ) 86 | elif SOFA_type == 1: 87 | progress(0.3, '2.正在前往HubertFA生成TextGrid') 88 | sys.path.append('HubertFA') 89 | from HubertFA import infer 90 | print(f'--ckpt {os.path.abspath(sofa_model)} --folder {wav_path} --language {ds_dict.split('\\')[-1].split('/')[-1].split('.')[0]} --dictionary {os.path.abspath(ds_dict)} --save_confidence') 91 | with click.Context(infer.main) as ctx: 92 | result = ctx.invoke( 93 | infer.main, 94 | ckpt=os.path.abspath(sofa_model), 95 | folder=wav_path, 96 | language=ds_dict.split('\\')[-1].split('/')[-1].split('.')[0], 97 | dictionary=os.path.abspath(ds_dict), 98 | save_confidence=True 99 | ) 100 | del_SP.process_all_textgrid_files(wav_path+'/TextGrid') 101 | print('已执行HubertFA') 102 | VCV_mode = config['VCV_mode'] 103 | if not VCV_mode: 104 | VCV_mode = '0' 105 | progress(0.4,'3.生成json') 106 | TextGrid2ds_json.run(config['TextGrid_path']) 107 | ds_json2filter.run(config['ds_dict'], config['TextGrid_path'] + '/json/ds_phone.json', config['ignore']) 108 | progress(0.5,'3.生成word.json') 109 | ds_json2word.run(config['ds_dict'], config['TextGrid_path'] + '/json/ds_phone_filter.json') 110 | progress(0.6,'6.生成oto.ini') 111 | if VCV_mode == '1': 112 | progress(0.6,'生成模式:VCV') 113 | json2VCV_oto.run(config['presamp'], config['TextGrid_path'] + '/json/utau_phone.json', 114 | config['TextGrid_path'] + '/json/word_phone.json', 115 | config['wav_path'], config['cv_sum'], config['vc_sum'], config['vv_sum']) 116 | elif VCV_mode == '2': 117 | progress(0.6,'生成模式:CVV') 118 | json2CV_oto.run(config['presamp'], config['TextGrid_path'] + '/json/utau_phone.json', 119 | config['TextGrid_path'] + '/json/word_phone.json', 120 | config['wav_path'], config['cv_sum'], config['vc_sum'], config['vv_sum']) 121 | elif VCV_mode == '0': 122 | progress(0.6,'生成模式:CVVC') 123 | json2oto.run(config['presamp'], config['TextGrid_path'] + '/json/utau_phone.json', 124 | config['TextGrid_path'] + '/json/word_phone.json', 125 | config['wav_path'], config['cv_sum'], config['vc_sum'], config['vv_sum']) 126 | else: 127 | progress(0.6,'VCV_mode数值错误') 128 | input(0.6,'退出') 129 | return 'VCV_mode数值错误' 130 | progress(0.7,'7.读取CV和VC oto.ini') 131 | cv = oto_rw.oto_read(config['wav_path'] + '/cv_oto.ini') 132 | vc = oto_rw.oto_read(config['wav_path'] + '/vc_oto.ini') 133 | progress(0.7,'8.剔除重复项') 134 | cv = oto_rw.oto_repeat(cv, int(config['CV_repeat'])) 135 | vc = oto_rw.oto_repeat(vc, int(config['VC_repeat'])) 136 | progress(0.7,'9.偏移oto数值.ini') 137 | if config['cv_offset'] != [0.0, 0.0, 0.0, 0.0, 0.0]: 138 | cv = oto_rw.oto_offset(cv, config['cv_offset']) 139 | progress(0.7,'9.1.偏移CV数值,运行成功') 140 | if config['vc_offset'] != [0.0, 0.0, 0.0, 0.0, 0.0]: 141 | vc = oto_rw.oto_offset(vc, config['vc_offset']) 142 | progress(0.7,'9.1.偏移VC数值,运行成功') 143 | progress(0.8,'10.合并oto.ini') 144 | oto_rw.oto_write(config['wav_path'] + '/oto.ini', cv + vc, config['pitch'], config['cover']) 145 | progress(0.9,'11.检测缺少的音素') 146 | oto_check.run(config['wav_path'] + '/oto.ini', config['presamp'], config['pitch'], config['VCV_mode']) 147 | progress(1,"🎉 任务完成!最终结果:") 148 | return "🎉 任务完成!最终结果:去命令行窗口查看" 149 | 150 | 151 | # 定义文件夹选择函数 152 | def select_folder(): 153 | folder_path = filedialog.askdirectory(title="选择文件夹") # 打开文件夹选择对话框 154 | return folder_path 155 | 156 | 157 | def select_file(): 158 | file_path = filedialog.askopenfilename( 159 | title="选择文本或INI文件", 160 | filetypes=[("Text Files", "*.txt;*.ini")] 161 | ) 162 | return file_path 163 | 164 | def model_file(): 165 | file_path = filedialog.askopenfilename( 166 | title="选择模型文件", 167 | filetypes=[("Model Files", "*.ckpt;*.onnx")] # 限制文件类型[4,6](@ref) 168 | ) 169 | return file_path 170 | 171 | def update_params(voice_type): 172 | if voice_type == 0: 173 | return "1,3,1.5,1,2", "3,0,2,1,2", "3,3,1.5,1,3", "0,0,0,0,0", "0,0,0,0,0" 174 | elif voice_type == 1: 175 | return "1,3,1.5,1,2", "3,3,1.5,1,3,3", "0,0,0,0,0", "0,0,0,0,0", "0,0,0,0,0" 176 | elif voice_type == 2: 177 | return "1,3,1,1,2", "5,0,2,1,2", "0,0,0,0,0", "0,0,0,0,0", "0,0,0,0,0" 178 | elif voice_type == 3: 179 | return "0,0,0,0,0", "0,0,0,0,0", "0,0,0,0,0", "0,0,0,0,0", "0,0,0,0,0" 180 | else: 181 | return "0,0,1.5,1,2", "3,0,2,1,2", "3,3,1.5,1,3", "0,0,0,0,0", "0,0,0,0,0" 182 | 183 | def scan_model_folder(SOFA_type): 184 | model_dir = "HubertFA_model" 185 | if SOFA_type == 0: 186 | model_dir = "SOFA_model" 187 | elif SOFA_type == 1: 188 | model_dir = "HubertFA_model" 189 | if os.path.exists(model_dir) and os.path.isdir(model_dir): 190 | sub_folders = [d for d in os.listdir(model_dir) if os.path.isdir(os.path.join(model_dir, d))] 191 | print(sub_folders) 192 | return gr.Dropdown(choices=sub_folders) 193 | return [] 194 | 195 | def scan_presamp_folder(): 196 | presamp = "presamp" 197 | if presamp: 198 | all_files = [f for f in os.listdir(presamp) if os.path.isfile(os.path.join(presamp, f))] 199 | print(all_files) 200 | return all_files 201 | return [] 202 | 203 | # 定义选择文件夹后更新 ds_dict 和 sofa_model 的函数 204 | def update_model_paths(SOFA_type, selected_folder): 205 | model_dir = "HubertFA_model" 206 | if SOFA_type == 0: 207 | model_dir = "SOFA_model" 208 | elif SOFA_type == 1: 209 | model_dir = "HubertFA_model" 210 | 211 | folder_path = os.path.join(model_dir, selected_folder) 212 | txt_files = [f for f in os.listdir(folder_path) if f.endswith('.txt')] 213 | ckpt_files = [f for f in os.listdir(folder_path) if f.endswith('.ckpt')] 214 | 215 | ds_dict_path = os.path.join(folder_path, txt_files[0]) if txt_files else "" 216 | sofa_model_path = os.path.join(folder_path, ckpt_files[0]) if ckpt_files else "" 217 | ds_dict_path = os.path.abspath(ds_dict_path) 218 | sofa_model_path = os.path.abspath(sofa_model_path) 219 | # 使用 update() 方法更新下拉选项 220 | return ( 221 | ds_dict_path, 222 | sofa_model_path, 223 | gr.Dropdown(choices=txt_files, value=txt_files[0]), 224 | gr.Dropdown(choices=ckpt_files, value=ckpt_files[0]) 225 | ) 226 | 227 | def update_dict_paths(sofa_model,dict_folders): 228 | folder_path = os.path.dirname(sofa_model) 229 | folder_path = os.path.join(folder_path, dict_folders) 230 | folder_path = os.path.abspath(folder_path) 231 | return folder_path 232 | 233 | def update_model_version_paths(sofa_model,model_version_folder_selector): 234 | folder_path = os.path.dirname(sofa_model) 235 | folder_path = os.path.join(folder_path, model_version_folder_selector) 236 | folder_path = os.path.abspath(folder_path) 237 | return folder_path 238 | 239 | def update_presamp_paths(selected_folder): 240 | folder_path = os.path.join("presamp", selected_folder) 241 | folder_path = os.path.abspath(folder_path) 242 | return folder_path 243 | 244 | with gr.Blocks(title="UTAU 参数生成器") as demo: 245 | gr.Markdown("### 必填参数配置") 246 | with gr.Row(equal_height=True): 247 | with gr.Row(equal_height=True): 248 | with gr.Column(scale=3,min_width=150): 249 | wav_path = gr.Textbox(label="音源wav路径", placeholder="输入文件夹路径") 250 | with gr.Column(scale=2,min_width=150): 251 | folder_btn = gr.Button("选择文件夹", variant="primary") 252 | with gr.Row(equal_height=True): 253 | with gr.Column(scale=3,min_width=150): 254 | presamp = gr.Textbox(label="presamp.ini路径",placeholder="输入文件路径") 255 | with gr.Column(scale=2,min_width=150): 256 | presamp_btn = gr.Button("选择文件", variant="primary") 257 | with gr.Row(equal_height=True): 258 | SOFA_mode = gr.Radio( 259 | choices=[("开启", 0), ("关闭", 1)], # (显示文本, 实际值) 260 | value=0, # 默认选中值 261 | label="是否生成TextGrid" 262 | ) 263 | SOFA_type = gr.Radio( 264 | choices=[("SOFA", 0), ("HubertFA", 1)], # (显示文本, 实际值) 265 | value=1, # 默认选中值 266 | label="选择标记程序" 267 | ) 268 | 269 | model_folder_selector = gr.Dropdown(choices=[], label="选择模型文件夹",value='') 270 | model_version_folder_selector = gr.Dropdown(choices=[], label="选择模型",value='') 271 | dict_folders_selector = gr.Dropdown(choices=[], label="选择模型字典",value='') 272 | model_presamp = scan_presamp_folder() 273 | model_presamp_selector = gr.Dropdown(choices=model_presamp, label="选择presamp(优先使用录音表提供的)",value='') 274 | 275 | with gr.Row(equal_height=True): 276 | with gr.Row(equal_height=True): 277 | with gr.Column(scale=3,min_width=150): 278 | sofa_model = gr.Textbox(label="sofa模型路径",placeholder="输入文件路径") 279 | with gr.Column(scale=2,min_width=150): 280 | model_btn = gr.Button("选择文件", variant="primary") 281 | with gr.Row(equal_height=True): 282 | with gr.Column(scale=3,min_width=150): 283 | ds_dict = gr.Textbox(label="sofa字典路径",placeholder="输入模型路径") 284 | with gr.Column(scale=2,min_width=150): 285 | ds_dict_btn = gr.Button("选择文件", variant="primary") 286 | 287 | with gr.Row(): 288 | VCV_mode = gr.Radio( 289 | choices=[("CVVC", 0), ("VCV", 1), ("CV", 2), ("Test", 3)], # (显示文本, 实际值) 290 | value=0, # 默认选中值 291 | label="音源类型" 292 | ) 293 | pitch = gr.Textbox(label="音阶后缀",placeholder="例如: F3") 294 | 295 | with gr.Row(): 296 | lab = gr.Radio(choices=["Y", "N"], value="Y", label="生成lab文件") 297 | cover = gr.Radio(choices=["Y", "N"], value="Y", label="覆盖oto") 298 | with gr.Row(): 299 | cut = gr.Textbox(label="字符分隔符", value="_,-") 300 | ignore = gr.Textbox(label="忽略的sofa音素", value="AP,SP") 301 | 302 | gr.Markdown("### 可选参数配置") 303 | with gr.Accordion("高级参数配置", open=False): 304 | gr.Markdown("**规则参数(逗号分隔数值)​**​") 305 | with gr.Row(): 306 | cv_sum = gr.Textbox(label="CV规则比例", value="1,3,1.5,1,2") 307 | vc_sum = gr.Textbox(label="VC规则比例", value="3,3,1.5,1,3,3") 308 | vv_sum = gr.Textbox(label="VV规则比例", value="3,3,1.5,1,3") 309 | 310 | cv_offset = gr.Textbox(label="CV数值偏移量", value="0,0,0,0,0") 311 | vc_offset = gr.Textbox(label="VC数值偏移量", value="0,0,0,0,0") 312 | with gr.Row(): 313 | CV_repeat = gr.Textbox(label="CV重复次数", value="1") 314 | VC_repeat = gr.Textbox(label="VC重复次数", value="1") 315 | # 定义更新参数的函数 316 | 317 | demo.load( 318 | fn=scan_model_folder, 319 | inputs=SOFA_type, 320 | outputs=model_folder_selector 321 | ) 322 | 323 | SOFA_type.change( 324 | fn=scan_model_folder, 325 | inputs=SOFA_type, 326 | outputs=model_folder_selector 327 | ) 328 | 329 | model_folder_selector.change( 330 | fn=update_model_paths, 331 | inputs=[SOFA_type,model_folder_selector], 332 | outputs=[ds_dict, sofa_model,dict_folders_selector,model_version_folder_selector] 333 | ) 334 | dict_folders_selector.change( 335 | fn=update_dict_paths, 336 | inputs= [sofa_model,dict_folders_selector], 337 | outputs=ds_dict 338 | ) 339 | model_version_folder_selector.change( 340 | fn=update_model_version_paths, 341 | inputs=[sofa_model,model_version_folder_selector], 342 | outputs=sofa_model 343 | ) 344 | model_presamp_selector.change( 345 | fn=update_presamp_paths, 346 | inputs=model_presamp_selector, 347 | outputs=presamp 348 | ) 349 | # 按钮点击事件绑定 350 | folder_btn.click( 351 | fn=select_folder, # 调用文件夹选择函数 352 | outputs=wav_path, # 将结果传递给文本框 353 | ) 354 | 355 | # 按钮点击事件绑定 356 | presamp_btn.click( 357 | fn=select_file, # 调用文件夹选择函数 358 | outputs=presamp, # 将结果传递给文本框 359 | ) 360 | 361 | ds_dict_btn.click( 362 | fn=select_file, # 调用文件夹选择函数 363 | outputs=ds_dict, # 将结果传递给文本框 364 | ) 365 | 366 | model_btn.click( 367 | fn=model_file, # 调用文件夹选择函数 368 | outputs=sofa_model, # 将结果传递给文本框 369 | ) 370 | 371 | VCV_mode.change( 372 | fn=update_params, 373 | inputs=VCV_mode, 374 | outputs=[cv_sum, vc_sum, vv_sum, cv_offset, vc_offset] 375 | ) 376 | 377 | btn = gr.Button("生成配置", variant="primary") 378 | output = gr.Textbox(label="生成结果",lines=10) 379 | 380 | btn.click( 381 | fn=generate_config, 382 | inputs=[ 383 | wav_path, ds_dict, presamp, cut, ignore, 384 | VCV_mode, lab, cv_sum, vc_sum, vv_sum, 385 | cv_offset, vc_offset, pitch, CV_repeat, 386 | VC_repeat, cover,sofa_model,SOFA_mode,SOFA_type 387 | ], 388 | outputs=output 389 | ) 390 | 391 | if __name__ == "__main__": 392 | run() -------------------------------------------------------------------------------- /config/run-config-CV.txt: -------------------------------------------------------------------------------- 1 | #wav路径(必改) 2 | wav_path=F3 3 | #sofa模型对应ds字典路径(必改) 4 | ds_dict=opencpop-extension.txt 5 | #presamp.ini的路径(必改) 6 | presamp=presamp/CVR-presamp.ini 7 | 8 | #字符分隔符 9 | cut=_,- 10 | #忽略ds音素 11 | ignore=AP,SP 12 | #sofa2utau字典音素映射(还没做,咕咕咕了) 13 | sofa2utau=sofa2utau.txt 14 | #CVVC:0 VCV:1 CV(多字单独音(连单音)):2(因为单字单独音,sofa标不太准,所以建议你用连单音录制方式) 15 | VCV_mode=2 16 | #是否生成lab,y or Y 生成,其他不生成 17 | lab=Y 18 | #-CV和CV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 19 | cv_sum=1,3,1,1,2 20 | #VC和VV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 21 | vc_sum=5,0,2,1,2 22 | vv_sum=0,0,0,0,0 23 | #偏移数值(左-右+,单位ms) 24 | #(左线偏移后,其他线都要自己进行同步偏移数值) 25 | #(右线的数值,在处理前会自动转为正数,所以不需要考虑正负问题) 26 | #示例:cv_sum=10,-10,-10,-10,-10(这样调整才能保持线位置不变) 27 | #-CV和CV规则:左线偏移,固定偏移,右线偏移,预发声偏移,交叉偏移 28 | cv_offset=0,0,0,0,-50 29 | #VC和VV规则:左线偏移,固定的偏移,右线偏移,预发声偏移,交叉偏移 30 | vc_offset=0,0,0,0,0 31 | #音阶后缀 32 | pitch= 33 | #保留多少个重复音素 34 | CV_repeat=1 35 | VC_repeat=0 36 | #是否覆盖oto,Y or y覆盖,其他不覆盖 37 | cover=Y 38 | 39 | -------------------------------------------------------------------------------- /config/run-config-CVR.txt: -------------------------------------------------------------------------------- 1 | #wav路径(必改) 2 | wav_path=F3 3 | #sofa模型对应ds字典路径(必改) 4 | ds_dict=opencpop-extension.txt 5 | #presamp.ini的路径(必改) 6 | presamp=presamp/CVR-presamp.ini 7 | 8 | #字符分隔符 9 | cut=_,- 10 | #忽略ds音素 11 | ignore=AP,SP 12 | #sofa2utau字典音素映射(还没做,咕咕咕了) 13 | sofa2utau=sofa2utau.txt 14 | #CVVC:0 VCV:1 CV(多字单独音(连单音)):2(因为单字单独音,sofa标不太准,所以建议你用连单音录制方式) 15 | VCV_mode=2 16 | #是否生成lab,y or Y 生成,其他不生成 17 | lab=Y 18 | #-CV和CV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 19 | cv_sum=1,3,1.5,1,2 20 | #VC和VV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 21 | vc_sum=3,0,2,1,2 22 | vv_sum=3,3,1.5,1,1.5 23 | #偏移数值(左-右+,单位ms) 24 | #(左线偏移后,其他线都要自己进行同步偏移数值) 25 | #(右线的数值,在处理前会自动转为正数,所以不需要考虑正负问题) 26 | #示例:cv_sum=10,-10,-10,-10,-10(这样调整才能保持线位置不变) 27 | #-CV和CV规则:左线偏移,固定偏移,右线偏移,预发声偏移,交叉偏移 28 | cv_offset=10,-10,-10,-10,-10 29 | #VC和VV规则:左线偏移,固定的偏移,右线偏移,预发声偏移,交叉偏移 30 | vc_offset=-50,0,0,0,0 31 | #音阶后缀 32 | pitch= 33 | #保留多少个重复音素 34 | CV_repeat=1 35 | VC_repeat=1 36 | #是否覆盖oto,Y or y覆盖,其他不覆盖 37 | cover=Y 38 | 39 | -------------------------------------------------------------------------------- /config/run-config-CVVC.txt: -------------------------------------------------------------------------------- 1 | #wav路径(必改) 2 | wav_path=F3 3 | #sofa模型对应ds字典路径(必改) 4 | ds_dict=opencpop-extension.txt 5 | #presamp.ini的路径(必改) 6 | presamp=presamp/CVR-presamp.ini 7 | 8 | #字符分隔符 9 | cut=_,- 10 | #忽略ds音素 11 | ignore=AP,SP 12 | #sofa2utau字典音素映射(咕咕咕了,直接修改sofa的字典添加单词即可) 13 | sofa2utau=sofa2utau.txt 14 | #CVVC:0 VCV:1 CV(多字单独音(连单音)):2(因为单字单独音,sofa标不太准,所以建议你用连单音录制方式) 15 | VCV_mode=0 16 | #是否生成lab,y or Y 生成,其他不生成 17 | lab=Y 18 | #-CV和CV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 19 | cv_sum=1,3,1.5,1,2 20 | #VC和VV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 21 | vc_sum=3,0,2,1,2 22 | vv_sum=3,3,1.5,1,3 23 | #偏移数值(左+右-,单位ms) 24 | #(左线偏移后,其他线都要自己进行同步偏移数值) 25 | #(右线的数值,在处理前会自动转为正数,所以不需要考虑正负问题) 26 | #示例:cv_sum=10,-10,-10,-10,-10(这样调整才能保持线位置不受改变) 27 | #-CV和CV规则:左线偏移,固定偏移,右线偏移,预发声偏移,交叉偏移 28 | cv_offset=0,0,0,0,0 29 | #VC和VV规则:左线偏移,固定的偏移,右线偏移,预发声偏移,交叉偏移 30 | vc_offset=0,0,0,0,0 31 | #音阶后缀 32 | pitch= 33 | #保留多少个重复音素 34 | CV_repeat=1 35 | VC_repeat=1 36 | #是否覆盖oto,Y or y覆盖,其他不覆盖 37 | cover=Y 38 | 39 | -------------------------------------------------------------------------------- /config/run-config-VCV.txt: -------------------------------------------------------------------------------- 1 | #wav路径(必改) 2 | wav_path=F3 3 | #sofa模型对应ds字典路径(必改) 4 | ds_dict=opencpop-extension.txt 5 | #presamp.ini的路径(必改) 6 | presamp=presamp/CVR-presamp.ini 7 | 8 | #字符分隔符 9 | cut=_,- 10 | #忽略ds音素 11 | ignore=AP,SP 12 | #sofa2utau字典音素映射(还没做,咕咕咕了) 13 | sofa2utau=sofa2utau.txt 14 | #CVVC:0 VCV:1 CV(多字单独音(连单音)):2(因为单字单独音,sofa标不太准,所以建议你用连单音录制方式) 15 | VCV_mode=1 16 | #是否生成lab,y or Y 生成,其他不生成 17 | lab=Y 18 | #-CV和CV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 19 | cv_sum=1,3,1.5,1,2 20 | #VC和VV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 21 | vc_sum=3,3,1.5,1,3,3 22 | vv_sum=0,0,0,0,0 23 | #偏移数值(左+右-,单位ms) 24 | #(左线偏移后,其他线都要自己进行同步偏移数值) 25 | #(右线的数值,在处理前会自动转为正数,所以不需要考虑正负问题) 26 | #示例:cv_sum=10,-10,-10,-10,-10(这样调整才能保持线位置不变) 27 | #-CV和CV规则:左线偏移,固定偏移,右线偏移,预发声偏移,交叉偏移 28 | cv_offset=0,0,0,0,0 29 | #VC和VV规则:左线偏移,固定的偏移,右线偏移,预发声偏移,交叉偏移 30 | vc_offset=0,0,0,0,0 31 | #音阶后缀 32 | pitch= 33 | #保留多少个重复音素 34 | CV_repeat=1 35 | VC_repeat=1 36 | #是否覆盖oto,Y or y覆盖,其他不覆盖 37 | cover=Y 38 | 39 | -------------------------------------------------------------------------------- /dictionary/en.txt: -------------------------------------------------------------------------------- 1 | aa aa 2 | ae ae 3 | ah ah 4 | ao ao 5 | aw aw 6 | ax ax 7 | ay ay 8 | b b 9 | ch ch 10 | d d 11 | dh dh 12 | dr dr 13 | dx dx 14 | eh eh 15 | er er 16 | ey ey 17 | f f 18 | g g 19 | hh hh 20 | ih ih 21 | iy iy 22 | jh jh 23 | k k 24 | l l 25 | m m 26 | n n 27 | ng ng 28 | ow ow 29 | oy oy 30 | p p 31 | q q 32 | r r 33 | s s 34 | sh sh 35 | t t 36 | th th 37 | tr tr 38 | uh uh 39 | uw uw 40 | v v 41 | vf vf 42 | w w 43 | y y 44 | z z 45 | zh zh 46 | i i 47 | N N 48 | M M 49 | oh oh -------------------------------------------------------------------------------- /dictionary/hira2roma_list.txt: -------------------------------------------------------------------------------- 1 | いぇ,ye_ 2 | 3 | うぇ,we_ 4 | うぃ,wi_ 5 | うぉ,wo_ 6 | 7 | きゃ,kya_ 8 | きゅ,kyu_ 9 | きぇ,kye_ 10 | きょ,kyo_ 11 | ぎゃ,gya_ 12 | ぎゅ,gyu_ 13 | ぎぇ,gye_ 14 | ぎょ,gyo_ 15 | 16 | くぁ,kwa_ 17 | くぃ,kwi_ 18 | くぇ,kwe_ 19 | くぉ,kwo_ 20 | ぐぁ,gwa_ 21 | ぐぃ,gwi_ 22 | ぐぇ,gwe_ 23 | ぐぉ,gwo_ 24 | 25 | しゃ,sha_ 26 | しゅ,shu_ 27 | しぇ,she_ 28 | しょ,sho_ 29 | じゃ,ja_ 30 | じぇ,je_ 31 | じゅ,ju_ 32 | じょ,jo_ 33 | 34 | すぃ,si_ 35 | ずぃ,zi_ 36 | 37 | ちゃ,cha_ 38 | ちゅ,chu_ 39 | ちぇ,che_ 40 | ちょ,cho_ 41 | 42 | つぁ,tsa_ 43 | つぃ,tsi_ 44 | つぇ,tse_ 45 | つぉ,tso_ 46 | 47 | てぃ,ti_ 48 | てゅ,tyu_ 49 | でぃ,di_ 50 | でゅ,dyu_ 51 | とぅ,tu_ 52 | どぅ,du_ 53 | 54 | にゃ,nya_ 55 | にぇ,nye_ 56 | にゅ,nyu_ 57 | にょ,nyo_ 58 | 59 | ひゃ,hya_ 60 | ひゅ,hyu_ 61 | ひぇ,hye_ 62 | ひょ,hyo_ 63 | 64 | びゃ,bya_ 65 | びゅ,byu_ 66 | びぇ,bye_ 67 | びょ,byo_ 68 | 69 | ぴゃ,pya_ 70 | ぴゅ,pyu_ 71 | ぴぇ,pye_ 72 | ぴょ,pyo_ 73 | 74 | ふぁ,fa_ 75 | ふぃ,fi_ 76 | ふぇ,fe_ 77 | ふぉ,fo_ 78 | ぶぁ,bwa_ 79 | ぶぃ,bwi_ 80 | ぶぇ,bwe_ 81 | ぶぉ,bwo_ 82 | 83 | みゃ,mya_ 84 | みゅ,myu_ 85 | みぇ,mye_ 86 | みょ,myo_ 87 | 88 | りゃ,rya_ 89 | りゅ,ryu_ 90 | りぇ,rye_ 91 | りょ,ryo_ 92 | 93 | ヴぁ,va_ 94 | ヴぃ,vi_ 95 | ヴぇ,ve_ 96 | ヴぉ,vo_ 97 | ヴ,vu_ 98 | 99 | ガ,nga_ 100 | ギ,ngi_ 101 | グ,ngu_ 102 | ゲ,nge_ 103 | ゴ,ngo_ 104 | 105 | あ,a_ 106 | い,i_ 107 | う,u_ 108 | え,e_ 109 | お,o_ 110 | 111 | か,ka_ 112 | が,ga_ 113 | き,ki_ 114 | ぎ,gi_ 115 | く,ku_ 116 | ぐ,gu_ 117 | け,ke_ 118 | げ,ge_ 119 | こ,ko_ 120 | ご,go_ 121 | 122 | さ,sa_ 123 | ざ,za_ 124 | し,shi_ 125 | じ,ji_ 126 | す,su_ 127 | ず,zu_ 128 | せ,se_ 129 | ぜ,ze_ 130 | そ,so_ 131 | ぞ,zo_ 132 | 133 | た,ta_ 134 | だ,da_ 135 | ち,chi_ 136 | つ,tsu_ 137 | て,te_ 138 | で,de_ 139 | と,to_ 140 | ど,do_ 141 | 142 | な,na_ 143 | に,ni_ 144 | ぬ,nu_ 145 | ね,ne_ 146 | の,no_ 147 | 148 | は,ha_ 149 | ば,ba_ 150 | ぱ,pa_ 151 | ひ,hi_ 152 | び,bi_ 153 | ぴ,pi_ 154 | ふ,fu_ 155 | ぶ,bu_ 156 | ぷ,pu_ 157 | へ,he_ 158 | べ,be_ 159 | ぺ,pe_ 160 | ほ,ho_ 161 | ぼ,bo_ 162 | ぽ,po_ 163 | 164 | ま,ma_ 165 | み,mi_ 166 | む,mu_ 167 | め,me_ 168 | も,mo_ 169 | 170 | や,ya_ 171 | ゆ,yu_ 172 | よ,yo_ 173 | 174 | ら,ra_ 175 | り,ri_ 176 | る,ru_ 177 | れ,re_ 178 | ろ,ro_ 179 | 180 | わ,wa_ 181 | ん,N_ 182 | 183 | -------------------------------------------------------------------------------- /dictionary/ja.txt: -------------------------------------------------------------------------------- 1 | R R 2 | B B 3 | いぇ y e 4 | うぇ w e 5 | うぃ w i 6 | うぉ w o 7 | きゃ ky a 8 | きゅ ky u 9 | きぇ ky e 10 | きょ ky o 11 | ぎゃ gy a 12 | ぎゅ gy u 13 | ぎぇ gy e 14 | ぎょ gy o 15 | くぁ kw a 16 | くぃ kw i 17 | くぇ kw e 18 | くぉ kw o 19 | ぐぁ gw a 20 | ぐぃ gw i 21 | ぐぇ gw e 22 | ぐぉ gw o 23 | しゃ sh a 24 | しゅ sh u 25 | しぇ sh e 26 | しょ sh o 27 | じゃ j a 28 | じぇ j e 29 | じゅ j u 30 | じょ j o 31 | すぃ s i 32 | ずぃ z i 33 | ちゃ ch a 34 | ちゅ ch u 35 | ちぇ ch e 36 | ちょ ch o 37 | つぁ ts a 38 | つぃ ts i 39 | つぇ ts e 40 | つぉ ts o 41 | てぃ ty i 42 | てゅ ty u 43 | でぃ dy i 44 | でゅ dy u 45 | とぅ t u 46 | どぅ d u 47 | にゃ ny a 48 | にぇ ny e 49 | にゅ ny u 50 | にょ ny o 51 | ひゃ hy a 52 | ひゅ hy u 53 | ひぇ hy e 54 | ひょ hy o 55 | びゃ by a 56 | びゅ by u 57 | びぇ by e 58 | びょ by o 59 | ぴゃ py a 60 | ぴゅ py u 61 | ぴぇ py e 62 | ぴょ py o 63 | ふぁ f a 64 | ふぃ f i 65 | ふぇ f e 66 | ふぉ f o 67 | みゃ my a 68 | みゅ my u 69 | みぇ my e 70 | みょ my o 71 | りゃ ry a 72 | りゅ ry u 73 | りぇ ry e 74 | りょ ry o 75 | ヴぁ v a 76 | ヴぃ v i 77 | ヴぇ v e 78 | ヴぉ v o 79 | ヴ v u 80 | ガ ng a 81 | ギ ng i 82 | グ ng u 83 | ゲ ng e 84 | ゴ ng o 85 | あ a 86 | い i 87 | う u 88 | え e 89 | お o 90 | か k a 91 | が g a 92 | き ky i 93 | ぎ gy i 94 | く kw u 95 | ぐ gw u 96 | け k e 97 | げ g e 98 | こ k o 99 | ご g o 100 | さ s a 101 | ざ z a 102 | し sh i 103 | じ j i 104 | す s u 105 | ず z u 106 | せ s e 107 | ぜ z e 108 | そ s o 109 | ぞ z o 110 | た t a 111 | だ d a 112 | ち ch i 113 | つ ts u 114 | て t e 115 | で d e 116 | と t o 117 | ど d o 118 | な n a 119 | に ny i 120 | ぬ n u 121 | ね n e 122 | の n o 123 | は h a 124 | ば b a 125 | ぱ p a 126 | ひ hy i 127 | び by i 128 | ぴ py i 129 | ふ f u 130 | ぶ b u 131 | ぷ p u 132 | へ h e 133 | べ b e 134 | ぺ p e 135 | ほ h o 136 | ぼ b o 137 | ぽ p o 138 | ま m a 139 | み my i 140 | む m u 141 | め m e 142 | も m o 143 | や y a 144 | ゆ y u 145 | よ y o 146 | ら r a 147 | り ry i 148 | る r u 149 | れ r e 150 | ろ r o 151 | わ w a 152 | ん N 153 | -------------------------------------------------------------------------------- /dictionary/zh.txt: -------------------------------------------------------------------------------- 1 | R R 2 | B B 3 | a a 4 | ai ai 5 | an an 6 | ang ang 7 | ao ao 8 | ba b a 9 | bai b ai 10 | ban b an 11 | bang b ang 12 | bao b ao 13 | be b e 14 | bei b ei 15 | ben b en 16 | beng b eng 17 | ber b er 18 | bi b i 19 | bia b ia 20 | bian b ian 21 | biang b iang 22 | biao b iao 23 | bie b ie 24 | bin b in 25 | bing b ing 26 | biong b iong 27 | biu b iu 28 | bo b o 29 | bong b ong 30 | bou b ou 31 | bu b u 32 | bua b ua 33 | buai b uai 34 | buan b uan 35 | buang b uang 36 | bui b ui 37 | bun b un 38 | bv b v 39 | bve b ve 40 | ca c a 41 | cai c ai 42 | can c an 43 | cang c ang 44 | cao c ao 45 | ce c e 46 | cei c ei 47 | cen c en 48 | ceng c eng 49 | cer c er 50 | cha ch a 51 | chai ch ai 52 | chan ch an 53 | chang ch ang 54 | chao ch ao 55 | che ch e 56 | chei ch ei 57 | chen ch en 58 | cheng ch eng 59 | cher ch er 60 | chi ch ir 61 | chong ch ong 62 | chou ch ou 63 | chu ch u 64 | chua ch ua 65 | chuai ch uai 66 | chuan ch uan 67 | chuang ch uang 68 | chui ch ui 69 | chun ch un 70 | chuo ch uo 71 | chv ch v 72 | chyi ch i 73 | ci c i0 74 | cong c ong 75 | cou c ou 76 | cu c u 77 | cua c ua 78 | cuai c uai 79 | cuan c uan 80 | cuang c uang 81 | cui c ui 82 | cun c un 83 | cuo c uo 84 | cv c v 85 | cyi c i 86 | da d a 87 | dai d ai 88 | dan d an 89 | dang d ang 90 | dao d ao 91 | de d e 92 | dei d ei 93 | den d en 94 | deng d eng 95 | der d er 96 | di d i 97 | dia d ia 98 | dian d ian 99 | diang d iang 100 | diao d iao 101 | die d ie 102 | din d in 103 | ding d ing 104 | diong d iong 105 | diu d iu 106 | dong d ong 107 | dou d ou 108 | du d u 109 | dua d ua 110 | duai d uai 111 | duan d uan 112 | duang d uang 113 | dui d ui 114 | dun d un 115 | duo d uo 116 | dv d v 117 | dve d ve 118 | e e 119 | ei ei 120 | en en 121 | eng eng 122 | er er 123 | fa f a 124 | fai f ai 125 | fan f an 126 | fang f ang 127 | fao f ao 128 | fe f e 129 | fei f ei 130 | fen f en 131 | feng f eng 132 | fer f er 133 | fi f i 134 | fia f ia 135 | fian f ian 136 | fiang f iang 137 | fiao f iao 138 | fie f ie 139 | fin f in 140 | fing f ing 141 | fiong f iong 142 | fiu f iu 143 | fo f o 144 | fong f ong 145 | fou f ou 146 | fu f u 147 | fua f ua 148 | fuai f uai 149 | fuan f uan 150 | fuang f uang 151 | fui f ui 152 | fun f un 153 | fv f v 154 | fve f ve 155 | ga g a 156 | gai g ai 157 | gan g an 158 | gang g ang 159 | gao g ao 160 | ge g e 161 | gei g ei 162 | gen g en 163 | geng g eng 164 | ger g er 165 | gi g i 166 | gia g ia 167 | gian g ian 168 | giang g iang 169 | giao g iao 170 | gie g ie 171 | gin g in 172 | ging g ing 173 | giong g iong 174 | giu g iu 175 | gong g ong 176 | gou g ou 177 | gu g u 178 | gua g ua 179 | guai g uai 180 | guan g uan 181 | guang g uang 182 | gui g ui 183 | gun g un 184 | guo g uo 185 | gv g v 186 | gve g ve 187 | ha h a 188 | hai h ai 189 | han h an 190 | hang h ang 191 | hao h ao 192 | he h e 193 | hei h ei 194 | hen h en 195 | heng h eng 196 | her h er 197 | hi h i 198 | hia h ia 199 | hian h ian 200 | hiang h iang 201 | hiao h iao 202 | hie h ie 203 | hin h in 204 | hing h ing 205 | hiong h iong 206 | hiu h iu 207 | hong h ong 208 | hou h ou 209 | hu h u 210 | hua h ua 211 | huai h uai 212 | huan h uan 213 | huang h uang 214 | hui h ui 215 | hun h un 216 | huo h uo 217 | hv h v 218 | hve h ve 219 | ji j i 220 | jia j ia 221 | jian j ian 222 | jiang j iang 223 | jiao j iao 224 | jie j ie 225 | jin j in 226 | jing j ing 227 | jiong j iong 228 | jiu j iu 229 | ju j v 230 | juan j van 231 | jue j ve 232 | jun j vn 233 | ka k a 234 | kai k ai 235 | kan k an 236 | kang k ang 237 | kao k ao 238 | ke k e 239 | kei k ei 240 | ken k en 241 | keng k eng 242 | ker k er 243 | ki k i 244 | kia k ia 245 | kian k ian 246 | kiang k iang 247 | kiao k iao 248 | kie k ie 249 | kin k in 250 | king k ing 251 | kiong k iong 252 | kiu k iu 253 | kong k ong 254 | kou k ou 255 | ku k u 256 | kua k ua 257 | kuai k uai 258 | kuan k uan 259 | kuang k uang 260 | kui k ui 261 | kun k un 262 | kuo k uo 263 | kv k v 264 | kve k ve 265 | la l a 266 | lai l ai 267 | lan l an 268 | lang l ang 269 | lao l ao 270 | le l e 271 | lei l ei 272 | len l en 273 | leng l eng 274 | ler l er 275 | li l i 276 | lia l ia 277 | lian l ian 278 | liang l iang 279 | liao l iao 280 | lie l ie 281 | lin l in 282 | ling l ing 283 | liong l iong 284 | liu l iu 285 | lo l o 286 | long l ong 287 | lou l ou 288 | lu l u 289 | lua l ua 290 | luai l uai 291 | luan l uan 292 | luang l uang 293 | lui l ui 294 | lun l un 295 | luo l uo 296 | lv l v 297 | lue l ve 298 | ma m a 299 | mai m ai 300 | man m an 301 | mang m ang 302 | mao m ao 303 | me m e 304 | mei m ei 305 | men m en 306 | meng m eng 307 | mer m er 308 | mi m i 309 | mia m ia 310 | mian m ian 311 | miang m iang 312 | miao m iao 313 | mie m ie 314 | min m in 315 | ming m ing 316 | miong m iong 317 | miu m iu 318 | mo m o 319 | mong m ong 320 | mou m ou 321 | mu m u 322 | mua m ua 323 | muai m uai 324 | muan m uan 325 | muang m uang 326 | mui m ui 327 | mun m un 328 | mv m v 329 | mve m ve 330 | na n a 331 | nai n ai 332 | nan n an 333 | nang n ang 334 | nao n ao 335 | ne n e 336 | nei n ei 337 | nen n en 338 | neng n eng 339 | ner n er 340 | ni n i 341 | nia n ia 342 | nian n ian 343 | niang n iang 344 | niao n iao 345 | nie n ie 346 | nin n in 347 | ning n ing 348 | niong n iong 349 | niu n iu 350 | nong n ong 351 | nou n ou 352 | nu n u 353 | nua n ua 354 | nuai n uai 355 | nuan n uan 356 | nuang n uang 357 | nui n ui 358 | nun n un 359 | nuo n uo 360 | nv n v 361 | nue n ve 362 | o o 363 | ong ong 364 | ou ou 365 | pa p a 366 | pai p ai 367 | pan p an 368 | pang p ang 369 | pao p ao 370 | pe p e 371 | pei p ei 372 | pen p en 373 | peng p eng 374 | per p er 375 | pi p i 376 | pia p ia 377 | pian p ian 378 | piang p iang 379 | piao p iao 380 | pie p ie 381 | pin p in 382 | ping p ing 383 | piong p iong 384 | piu p iu 385 | po p o 386 | pong p ong 387 | pou p ou 388 | pu p u 389 | pua p ua 390 | puai p uai 391 | puan p uan 392 | puang p uang 393 | pui p ui 394 | pun p un 395 | pv p v 396 | pve p ve 397 | qi q i 398 | qia q ia 399 | qian q ian 400 | qiang q iang 401 | qiao q iao 402 | qie q ie 403 | qin q in 404 | qing q ing 405 | qiong q iong 406 | qiu q iu 407 | qu q v 408 | quan q van 409 | que q ve 410 | qun q vn 411 | ra r a 412 | rai r ai 413 | ran r an 414 | rang r ang 415 | rao r ao 416 | re r e 417 | rei r ei 418 | ren r en 419 | reng r eng 420 | rer r er 421 | ri r ir 422 | rong r ong 423 | rou r ou 424 | ru r u 425 | rua r ua 426 | ruai r uai 427 | ruan r uan 428 | ruang r uang 429 | rui r ui 430 | run r un 431 | ruo r uo 432 | rv r v 433 | ryi r i 434 | sa s a 435 | sai s ai 436 | san s an 437 | sang s ang 438 | sao s ao 439 | se s e 440 | sei s ei 441 | sen s en 442 | seng s eng 443 | ser s er 444 | sha sh a 445 | shai sh ai 446 | shan sh an 447 | shang sh ang 448 | shao sh ao 449 | she sh e 450 | shei sh ei 451 | shen sh en 452 | sheng sh eng 453 | sher sh er 454 | shi sh ir 455 | shong sh ong 456 | shou sh ou 457 | shu sh u 458 | shua sh ua 459 | shuai sh uai 460 | shuan sh uan 461 | shuang sh uang 462 | shui sh ui 463 | shun sh un 464 | shuo sh uo 465 | shv sh v 466 | shyi sh i 467 | si s i0 468 | song s ong 469 | sou s ou 470 | su s u 471 | sua s ua 472 | suai s uai 473 | suan s uan 474 | suang s uang 475 | sui s ui 476 | sun s un 477 | suo s uo 478 | sv s v 479 | syi s i 480 | ta t a 481 | tai t ai 482 | tan t an 483 | tang t ang 484 | tao t ao 485 | te t e 486 | tei t ei 487 | ten t en 488 | teng t eng 489 | ter t er 490 | ti t i 491 | tia t ia 492 | tian t ian 493 | tiang t iang 494 | tiao t iao 495 | tie t ie 496 | tin t in 497 | ting t ing 498 | tiong t iong 499 | tong t ong 500 | tou t ou 501 | tu t u 502 | tua t ua 503 | tuai t uai 504 | tuan t uan 505 | tuang t uang 506 | tui t ui 507 | tun t un 508 | tuo t uo 509 | tv t v 510 | tve t ve 511 | wa w a 512 | wai w ai 513 | wan w an 514 | wang w ang 515 | wao w ao 516 | we w e 517 | wei w ei 518 | wen w en 519 | weng w eng 520 | wer w er 521 | wi w i 522 | wo w o 523 | wong w ong 524 | wou w ou 525 | wu w u 526 | u u 527 | xi x i 528 | xia x ia 529 | xian x ian 530 | xiang x iang 531 | xiao x iao 532 | xie x ie 533 | xin x in 534 | xing x ing 535 | xiong x iong 536 | xiu x iu 537 | xu x v 538 | xuan x van 539 | xue x ve 540 | xun x vn 541 | ya y a 542 | yai y ai 543 | yan y En 544 | yang y ang 545 | yao y ao 546 | ye y E 547 | yei y ei 548 | yi y i 549 | i i 550 | yin y in 551 | in in 552 | ying y ing 553 | ing ing 554 | yo y o 555 | yong y ong 556 | you y ou 557 | yu y v 558 | yuan y van 559 | yue y ve 560 | yun y vn 561 | ywu y u 562 | za z a 563 | zai z ai 564 | zan z an 565 | zang z ang 566 | zao z ao 567 | ze z e 568 | zei z ei 569 | zen z en 570 | zeng z eng 571 | zer z er 572 | zha zh a 573 | zhai zh ai 574 | zhan zh an 575 | zhang zh ang 576 | zhao zh ao 577 | zhe zh e 578 | zhei zh ei 579 | zhen zh en 580 | zheng zh eng 581 | zher zh er 582 | zhi zh ir 583 | zhong zh ong 584 | zhou zh ou 585 | zhu zh u 586 | zhua zh ua 587 | zhuai zh uai 588 | zhuan zh uan 589 | zhuang zh uang 590 | zhui zh ui 591 | zhun zh un 592 | zhuo zh uo 593 | zhv zh v 594 | zhyi zh i 595 | zi z i0 596 | zong z ong 597 | zou z ou 598 | zu z u 599 | zua z ua 600 | zuai z uai 601 | zuan z uan 602 | zuang z uang 603 | zui z ui 604 | zun z un 605 | zuo z uo 606 | zv z v 607 | zyi z i 608 | -------------------------------------------------------------------------------- /hira2roma.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | 5 | def main_process(): 6 | table_path = input(f"请输入转换表路径(回车使用默认值 sofa-dic/hira2roma_list.txt): ") 7 | if not os.path.exists(table_path): 8 | print(f"错误:转换表文件 {table_path} 不存在") 9 | table_path = "dictionary/hira2roma_list.txt" 10 | print(f"使用转换表:{table_path}") 11 | 12 | # 加载转换表 13 | print(f"\n正在加载转换表:{table_path}") 14 | hira_dict = {} 15 | try: 16 | with open(table_path, "r", encoding="utf-8") as f: 17 | for line_number, line in enumerate(f, 1): 18 | line = line.strip() 19 | if line and not line.startswith("#"): 20 | try: 21 | k, v = line.split(",", 1) 22 | hira_dict[k] = v 23 | except ValueError: 24 | print(f"警告:第 {line_number} 行格式错误 - {line}") 25 | print(f"成功加载 {len(hira_dict)} 个转换规则") 26 | except FileNotFoundError: 27 | print(f"错误:转换表文件 {table_path} 不存在") 28 | return 29 | 30 | # 按长度降序排序 31 | sorted_keys = sorted(hira_dict.keys(), key=lambda x: -len(x)) 32 | 33 | # 编码转换选项 34 | convert_encoding = input("需要将文件名编码从Shift_JIS转换为GBK吗?(y/n): ").lower() == "y" 35 | 36 | # 源目录输入 37 | src_root = input("请输入WAV目录路径: ") 38 | dest_root = os.path.join(src_root, "new_wav") 39 | print(f"目标目录设置为:{dest_root}") 40 | 41 | # 初始化统计 42 | total_files = 0 43 | converted_files = 0 44 | encoding_errors = 0 45 | 46 | # 遍历处理 47 | for root, dirs, files in os.walk(src_root): 48 | if "new_wav" in root: 49 | continue 50 | 51 | for filename in files: 52 | if filename.lower().endswith(".wav"): 53 | total_files += 1 54 | src_path = os.path.join(root, filename) 55 | 56 | # 编码转换 57 | if convert_encoding: 58 | try: 59 | # 修正为:GBK → Unicode → Shift_JIS(根据实际需求调整) 60 | decoded = filename.encode('gbk').decode('shiftjis') 61 | filename_gbk = decoded.encode('shiftjis', 'replace').decode('shiftjis') 62 | except UnicodeDecodeError: 63 | # 二次尝试:直接处理常见日文字符 64 | try: 65 | filename_gbk = filename.encode('shiftjis', 'replace').decode('shiftjis') 66 | except Exception as e: 67 | encoding_errors += 1 68 | print(f"编码转换失败:{filename},错误:{str(e)}") 69 | filename_gbk = filename 70 | except Exception as e: 71 | encoding_errors += 1 72 | print(f"编码转换失败:{filename},错误:{str(e)}") 73 | filename_gbk = filename 74 | else: 75 | filename_gbk=filename 76 | 77 | # 构建目标路径 78 | rel_path = os.path.relpath(root, src_root) 79 | dest_dir = os.path.join(dest_root, rel_path) 80 | os.makedirs(dest_dir, exist_ok=True) 81 | 82 | # 罗马字替换 83 | new_name = filename_gbk 84 | for k in sorted_keys: 85 | new_name = new_name.replace(k, hira_dict[k]) 86 | 87 | # 记录转换 88 | if new_name != filename_gbk: 89 | converted_files += 1 90 | print(f"重命名:{filename[:30]}... → {new_name[:30]}...") 91 | 92 | # 复制文件 93 | dest_path = os.path.join(dest_dir, new_name) 94 | try: 95 | shutil.copy2(src_path, dest_path) 96 | except Exception as e: 97 | print(f"文件复制失败:{src_path} → {str(e)}") 98 | 99 | # 输出统计 100 | print(f"\n处理完成!") 101 | print(f"扫描文件总数:{total_files}") 102 | print(f"执行重命名数:{converted_files}") 103 | print(f"编码转换失败数:{encoding_errors}") 104 | print(f"输出目录:{dest_root}") 105 | 106 | 107 | if __name__ == "__main__": 108 | main_process() -------------------------------------------------------------------------------- /img/TextGrid2oto.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaobaijunya/TextGrid2oto/5cdbe7e55b4145a25f680edf5365bc84b74dff17/img/TextGrid2oto.ico -------------------------------------------------------------------------------- /json2oto/json2CV_oto.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | 4 | 5 | #将json转换为oto 6 | #文件名=别名,左边界,固定,右边界(负值),预发声,交叉 7 | #autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 8 | #CV(V),VC(VV)(VR),-CV(-V) 9 | 10 | def presamp_read(presamps_path): 11 | V = {} 12 | with open(presamps_path, 'r',encoding='utf-8') as file: 13 | ini_text = file.read() 14 | # 提取 [VOWEL] 部分 15 | # 提取 [VOWEL] 部分 16 | vowel_match = re.search(r'\[VOWEL\](.*?)\[', ini_text, re.DOTALL) 17 | vowels = vowel_match.group(1).strip() 18 | # print(vowels) 19 | # 提取 [CONSONANT] 部分 20 | # consonant_match = re.search(r'\[CONSONANT\](.*?)\[', ini_text, re.DOTALL) 21 | # consonants = consonant_match.group(1).strip() 22 | # print(consonants) 23 | for vowel in vowels.split('\n'): 24 | v = vowel.split('=')[0] 25 | for v0 in vowel.split('=')[2].split(','): 26 | V[v0]=v 27 | # print(V) 28 | return V 29 | 30 | def json2cvoto(cv_data,sum): 31 | oto = [] 32 | for audio_file, data in cv_data.items(): 33 | autio_name = audio_file 34 | phones = data.get('phones', {}) 35 | long = data.get('long', []) 36 | if not phones: 37 | continue 38 | 39 | sorted_phones = sorted(phones.items(), key=lambda x: int(x[0])) 40 | #保证最后一个音符不会被忽略 41 | keyend, contend = sorted_phones[-1] 42 | if contend['text'] not in ['R', '-', 'AP', 'SP']: 43 | phone_name = contend['text'] 44 | # autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 45 | left = float(contend['xmin']) * 1000 / sum[0] 46 | 47 | Prevoice = (float(contend['middle']) - float(contend['xmin'])) * 1000 / sum[3] 48 | # 右线占比 49 | right = (float(contend['xmax']) - float(contend['middle'])) * 1000 / sum[2] + Prevoice 50 | # 固定的占比 51 | if sum[1] == 0: 52 | fixed = Prevoice 53 | else: 54 | fixed = Prevoice + (right - Prevoice) / sum[1] 55 | cross = float(Prevoice) / sum[4] 56 | 57 | oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 58 | 59 | i = 0 60 | while i < len(sorted_phones)-1: 61 | key, cont = sorted_phones[i] 62 | # -CV规则 63 | if cont['text'] in ['-','R']: 64 | key1, cont2 = sorted_phones[i + 1] 65 | phone_name = cont2['text'] 66 | # autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 67 | left = float(cont2['xmin'])*1000/sum[0] 68 | 69 | Prevoice = (float(cont2['middle']) - float(cont2['xmin'])) * 1000 / sum[3] 70 | #右线占比 71 | right = (float(cont2['xmax'])-float(cont2['middle']))*1000/sum[2] + Prevoice 72 | # 固定的占比 73 | if sum[1] ==0: 74 | fixed = Prevoice 75 | else: 76 | fixed = Prevoice+(right-Prevoice)/sum[1] 77 | cross = float(Prevoice)/sum[4] 78 | i+=2 79 | oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 80 | continue 81 | #CV规则 82 | phone_name =cont['text'] 83 | # autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 84 | left = float(cont['xmin']) * 1000 / sum[0] 85 | 86 | Prevoice = (float(cont['middle'])- float(cont['xmin'])) * 1000 / sum[3] 87 | # 右线占比 88 | right = (float(cont['xmax']) - float(cont['middle'])) * 1000 / sum[2] + Prevoice 89 | # 固定的占比 90 | if sum[1] == 0: 91 | fixed = Prevoice 92 | else: 93 | fixed = Prevoice+(right-Prevoice)/sum[1] 94 | cross = float(Prevoice) / sum[4] 95 | i += 1 96 | 97 | oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 98 | continue 99 | 100 | return oto 101 | 102 | 103 | def json2vcoto(vc_data,C_V,vc_sum): 104 | oto = [] 105 | for audio_file, data in vc_data.items(): 106 | autio_name = audio_file 107 | phones = data.get('phones', {}) 108 | long = data.get('long', []) 109 | if not phones: 110 | continue 111 | 112 | sorted_phones = sorted(phones.items(), key=lambda x: int(x[0])) 113 | i = 0 114 | for key, item in sorted_phones: 115 | if item['text'] in ('AP', 'SP'): 116 | # 修改 text 为 R 117 | item['text'] = 'R' 118 | 119 | while i < len(sorted_phones) - 1: 120 | key, cont = sorted_phones[i] 121 | key1, cont1 = sorted_phones[i + 1] 122 | # print(cont['text'], cont1['text'], cont2['text']) 123 | # 0V 1C 124 | # CC规则 125 | # 后续删除此功能 126 | if cont['text'] in C_V: 127 | phone_name = C_V[cont['text']] + ' ' + 'R' 128 | # autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 129 | left = float(cont["middle"]) * 1000 + ((float(cont['xmax']) - float(cont['middle'])) * 1000 / vc_sum[0]) 130 | # 右线占比 131 | Prevoice = float(cont['xmax']) * 1000 - left / vc_sum[3] 132 | right = Prevoice+50 133 | # 固定的占比 134 | if vc_sum[1] == 0: 135 | fixed = Prevoice 136 | else: 137 | fixed = Prevoice + (Prevoice-right) * 1000 / vc_sum[1] 138 | cross = (float(cont['xmax']) - float(cont['middle'])) * 1000 / vc_sum[4] 139 | # i += 1 140 | # print(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 141 | oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 142 | 143 | phone_name = '_'+C_V[cont['text']] 144 | left = float(cont["middle"]) * 1000 + ((float(cont['xmax']) - float(cont['middle'])) * 1000 / vc_sum[0]) 145 | # 右线占比 146 | right = float(cont['xmax']) * 1000 - left 147 | Prevoice = right / 4 148 | # 固定的占比 149 | fixed = (right - Prevoice) /4 +Prevoice 150 | cross = Prevoice / 2 151 | i += 1 152 | # print(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 153 | oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 154 | else: 155 | i+=1 156 | continue 157 | return oto 158 | 159 | 160 | 161 | def run(presamp_path,utau_phone_json,word_phone_json,wav_path,cv_sum,vc_sum,vv_sum): 162 | C_V = presamp_read(presamp_path) 163 | print(C_V) 164 | # with open(utau_phone_json, 'r', encoding='utf-8') as f: 165 | # vc_data = json.load(f) 166 | with open(word_phone_json, 'r', encoding='utf-8') as f: 167 | cv_data = json.load(f) 168 | oto = json2cvoto(cv_data,cv_sum) 169 | # print(oto) 170 | with open(wav_path+'/cv_oto.ini', 'w', encoding='utf-8') as f: 171 | for i in oto: 172 | f.write(i) 173 | print('cv_oto.ini生成成功') 174 | oto = json2vcoto(cv_data,C_V, vc_sum) 175 | # print(oto) 176 | with open(wav_path+'/vc_oto.ini', 'w', encoding='utf-8') as f: 177 | for i in oto: 178 | f.write(i) 179 | print('vc_oto.ini生成成功') 180 | 181 | 182 | if __name__ == '__main__': 183 | # presamp_path = 'risku中文CVVCpresamp.ini' 184 | # utau_phone = 'G:/编程/utau自动标注/F3/TextGrid/json/utau_phone.json' 185 | # word_phone_json = 'G:/编程/utau自动标注/F3/TextGrid/json/word_phone.json' 186 | # wav_path = 'G:/编程/utau自动标注/F3' 187 | 188 | presamp_path = '../presamp/樗儿式中文VCV-presamp.ini' 189 | utau_phone = 'E:\\OpenUtau\\Singers\空气音中文VCV_自动oto测试/VCV/TextGrid/json/utau_phone.json' 190 | word_phone_json = 'E:\\OpenUtau\\Singers\空气音中文VCV_自动oto测试/VCV/TextGrid/json/word_phone.json' 191 | wav_path = 'E:\\OpenUtau\\Singers\空气音中文VCV_自动oto测试\VCV' 192 | 193 | #-CV和CV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 194 | cv_sum = [1,3,1.5,1,2] 195 | #VC和VV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比,VV固定占比 196 | vc_sum=[3,0,2,1,2,3] 197 | vv_sum=[3,0,2,1,2,3] 198 | run(presamp_path,utau_phone,word_phone_json,wav_path,cv_sum,vc_sum,vv_sum) 199 | -------------------------------------------------------------------------------- /json2oto/json2VCV_oto.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | 4 | 5 | #将json转换为oto 6 | #文件名=别名,左边界,固定,右边界(负值),预发声,交叉 7 | #autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 8 | #CV(V),VC(VV)(VR),-CV(-V) 9 | 10 | def presamp_read(presamps_path): 11 | V = {} 12 | with open(presamps_path, 'r',encoding='utf-8') as file: 13 | ini_text = file.read() 14 | # 提取 [VOWEL] 部分 15 | # 提取 [VOWEL] 部分 16 | vowel_match = re.search(r'\[VOWEL\](.*?)\[', ini_text, re.DOTALL) 17 | vowels = vowel_match.group(1).strip() 18 | # print(vowels) 19 | # 提取 [CONSONANT] 部分 20 | # consonant_match = re.search(r'\[CONSONANT\](.*?)\[', ini_text, re.DOTALL) 21 | # consonants = consonant_match.group(1).strip() 22 | # print(consonants) 23 | for vowel in vowels.split('\n'): 24 | v = vowel.split('=')[0] 25 | for v0 in vowel.split('=')[2].split(','): 26 | V[v0]=v 27 | # print(V) 28 | return V 29 | 30 | def json2cvoto(cv_data,sum): 31 | oto = [] 32 | for audio_file, data in cv_data.items(): 33 | autio_name = audio_file 34 | phones = data.get('phones', {}) 35 | long = data.get('long', []) 36 | if not phones: 37 | continue 38 | 39 | sorted_phones = sorted(phones.items(), key=lambda x: int(x[0])) 40 | i = 0 41 | 42 | while i < len(sorted_phones)-1: 43 | key, cont = sorted_phones[i] 44 | # -CV规则 45 | if cont['text'] in ['-','R']: 46 | key1, cont2 = sorted_phones[i + 1] 47 | phone_name = '- '+cont2['text'] 48 | # autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 49 | left = float(cont2['xmin'])*1000/sum[0] 50 | 51 | Prevoice = (float(cont2['middle']) - float(cont2['xmin'])) * 1000 / sum[3] 52 | #右线占比 53 | right = (float(cont2['xmax'])-float(cont2['middle']))*1000/sum[2] + Prevoice 54 | # 固定的占比 55 | if sum[1] ==0: 56 | fixed = Prevoice 57 | else: 58 | fixed = Prevoice+(right-Prevoice)/sum[1] 59 | cross = float(Prevoice)/sum[4] 60 | i+=2 61 | # print(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 62 | oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 63 | continue 64 | # #CV规则 65 | # phone_name =cont['text'] 66 | # # autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 67 | # left = float(cont['xmin']) * 1000 / sum[0] 68 | # 69 | # Prevoice = (float(cont['middle'])- float(cont['xmin'])) * 1000 / sum[3] 70 | # # 右线占比 71 | # right = (float(cont['xmax']) - float(cont['middle'])) * 1000 / sum[2] + Prevoice 72 | # # 固定的占比 73 | # if sum[1] == 0: 74 | # fixed = Prevoice 75 | # else: 76 | # fixed = Prevoice+(right-Prevoice)/sum[1] 77 | # cross = float(Prevoice) / sum[4] 78 | i += 1 79 | # 80 | # oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 81 | continue 82 | 83 | return oto 84 | 85 | def json2VCVoto(cv_data,CV_V,sum): 86 | oto = [] 87 | for audio_file, data in cv_data.items(): 88 | autio_name = audio_file 89 | phones = data.get('phones', {}) 90 | long = data.get('long', []) 91 | if not phones: 92 | continue 93 | sorted_phones = sorted(phones.items(), key=lambda x: int(x[0])) 94 | i = 0 95 | while i < len(sorted_phones)-1: 96 | key, cont = sorted_phones[i] 97 | key1, cont1 = sorted_phones[i + 1] 98 | if cont['text'] in ['-','R']: 99 | i+=1 100 | continue 101 | elif cont1['text'] in ['R', 'B'] and cont['text'] in CV_V: 102 | phone_name = CV_V[cont['text']] + ' ' + cont1['text'] 103 | # autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 104 | left = float(cont["middle"]) * 1000 + ((float(cont['xmax']) - float(cont['middle'])) * 1000 / sum[0]) 105 | # 右线占比 106 | Prevoice = float(cont1['middle']) * 1000 - left / sum[3] 107 | right = (float(cont1['xmax'])-float(cont1['middle']))*1000/sum[2] + Prevoice 108 | # 固定的占比 109 | if sum[1] ==0: 110 | fixed = Prevoice 111 | else: 112 | fixed = Prevoice + (float(cont1['xmax']) - float(cont1['middle'])) * 1000 / sum[1] 113 | cross = (float(cont['xmax']) - float(cont['middle'])) * 1000 / sum[4] 114 | i += 1 115 | # print(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 116 | oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 117 | elif cont['text'] in CV_V and cont1['text'] in CV_V: 118 | phone_name = CV_V[cont['text']] + ' ' + cont1['text'] 119 | # autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 120 | left = float(cont["middle"]) * 1000 + ((float(cont['xmax']) - float(cont['middle'])) * 1000 / sum[0]) 121 | # 右线占比 122 | Prevoice = float(cont1['middle']) * 1000 - left / sum[3] 123 | right = (float(cont1['xmax'])-float(cont1['middle']))*1000/sum[2] + Prevoice 124 | # 固定的占比 125 | if sum[1] ==0: 126 | fixed = Prevoice 127 | else: 128 | fixed = Prevoice + (float(cont1['xmax']) - float(cont1['middle'])) * 1000 / sum[1] 129 | cross =(float(cont['xmax']) - float(cont['middle'])) *1000 / sum[4] 130 | i += 1 131 | oto.append(f"{autio_name}={phone_name},{left},{fixed},-{right},{Prevoice},{cross}\n") 132 | 133 | else: 134 | i+=1 135 | continue 136 | return oto 137 | 138 | def run(presamp_path,utau_phone_json,word_phone_json,wav_path,cv_sum,vc_sum,vv_sum): 139 | CV_V = presamp_read(presamp_path) 140 | CV_V['R']='R' 141 | print(CV_V) 142 | # with open(utau_phone_json, 'r', encoding='utf-8') as f: 143 | # vc_data = json.load(f) 144 | with open(word_phone_json, 'r', encoding='utf-8') as f: 145 | cv_data = json.load(f) 146 | oto = json2cvoto(cv_data,cv_sum) 147 | # print(oto) 148 | with open(wav_path+'/cv_oto.ini', 'w', encoding='utf-8') as f: 149 | for i in oto: 150 | f.write(i) 151 | print('cv_oto.ini生成成功') 152 | oto = json2VCVoto(cv_data,CV_V, vc_sum) 153 | # print(oto) 154 | with open(wav_path+'/vc_oto.ini', 'w', encoding='utf-8') as f: 155 | for i in oto: 156 | f.write(i) 157 | print('vc_oto.ini生成成功') 158 | 159 | 160 | if __name__ == '__main__': 161 | # presamp_path = 'risku中文CVVCpresamp.ini' 162 | # utau_phone = 'G:/编程/utau自动标注/F3/TextGrid/json/utau_phone.json' 163 | # word_phone_json = 'G:/编程/utau自动标注/F3/TextGrid/json/word_phone.json' 164 | # wav_path = 'G:/编程/utau自动标注/F3' 165 | 166 | # presamp_path = 'presamp/樗儿式中文VCV-presamp.ini' 167 | # utau_phone = 'E:\\OpenUtau\\Singers\空气音中文VCV_自动oto测试/VCV/TextGrid/json/utau_phone.json' 168 | # word_phone_json = 'E:\\OpenUtau\\Singers\空气音中文VCV_自动oto测试/VCV/TextGrid/json/word_phone.json' 169 | # wav_path = 'E:\\OpenUtau\\Singers\空气音中文VCV_自动oto测试\VCV' 170 | 171 | presamp_path = 'presamp/jp-VCV-romaji-presamp.ini' 172 | utau_phone = 'E:\OpenUtau\Singers\TNOT-Nottthat_VCV-TNOT-日语-VCV\\new_wav/TextGrid/json/utau_phone.json' 173 | word_phone_json = 'E:\OpenUtau\Singers\TNOT-Nottthat_VCV-TNOT-日语-VCV\\new_wav/TextGrid/json/word_phone.json' 174 | wav_path = 'E:\OpenUtau\Singers\TNOT-Nottthat_VCV-TNOT-日语-VCV\\new_wav' 175 | 176 | #-CV和CV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 177 | cv_sum = [1,3,1.5,1,2] 178 | #VC和VV规则:左线占比,固定的占比,右线占比,预发声不变,交叉占比 179 | vc_sum=[1,3,1.5,1,2] 180 | vv_sum=[0,0,0,0,0] 181 | run(presamp_path,utau_phone,word_phone_json,wav_path,cv_sum,vc_sum,vv_sum) 182 | -------------------------------------------------------------------------------- /json2oto/json2oto.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | 4 | 5 | #将json转换为oto 6 | #文件名=别名,左边界,固定,右边界(负值),预发声,交叉 7 | #autio_name=phone_name,left,fixed,right(负值),Prevoice,cross 8 | #CV(V),VC(VV)(VR),-CV(-V) 9 | 10 | def presamp_read(presamps_path): 11 | CV_V = {} 12 | CV_C = {} 13 | V_V = [] 14 | with open(presamps_path, 'r',encoding='utf-8') as file: 15 | ini_text = file.read() 16 | # 提取 [VOWEL] 部分 17 | # 提取 [VOWEL] 部分 18 | vowel_match = re.search(r'\[VOWEL\](.*?)\[', ini_text, re.DOTALL) 19 | vowels = vowel_match.group(1).strip() 20 | # print(vowels) 21 | # 提取 [CONSONANT] 部分 22 | consonant_match = re.search(r'\[CONSONANT\](.*?)\[', ini_text, re.DOTALL) 23 | consonants = consonant_match.group(1).strip() 24 | # print(consonants) 25 | for vowel in vowels.split('\n'): 26 | V0=(vowel.split('=')[0]) 27 | for vowel in vowel.split('=')[2].split(','): 28 | CV_V[vowel]=V0 29 | for consonant in consonants.split('\n'): 30 | C0=(consonant.split('=')[0]) 31 | for consonant in consonant.split('=')[1].split(','): 32 | CV_C[consonant]=C0 33 | V_V = [key for key in CV_V if key not in CV_C] 34 | print(f"CV_V:{CV_V}\nCV_C:{CV_C}\n,V_V:{V_V}") 35 | return CV_V, CV_C,V_V 36 | 37 | #VV_CV_VC 38 | def json2cvoto(cv_data,sum): 39 | oto = [] 40 | for audio_file, data in cv_data.items(): 41 | autio_name = audio_file 42 | phones = data.get('phones', {}) 43 | long = data.get('long', []) 44 | if not phones: 45 | continue 46 | 47 | sorted_phones = sorted(phones.items(), key=lambda x: int(x[0])) 48 | i = 0 49 | 50 | while i <= len(sorted_phones)-1: 51 | key, cont = sorted_phones[i] 52 | # -CV规则 53 | if cont['text'] in ['-','R'] and i1: 184 | for arg in sys.argv[1:]: 185 | auto_run(arg) 186 | quit() 187 | print('------------------------------') 188 | print('sofa-UTAU自动标注') 189 | print('1.手动生成oto模式') 190 | print('2.检测缺少的音素') 191 | A=input('请输入数字:') 192 | if A=='1': 193 | run_oto() 194 | elif A=='2': 195 | try: 196 | oto_path = input('请输入oto路径:') 197 | presamps_path = input('请输入presamps.ini路径:') 198 | pitch = input('请输入音阶后缀:') 199 | vcv_mode = input('请输入数字选择VCV模式:0-CVVC 1-VCV 2-CVV或CVVR') 200 | oto_check.run(oto_path, presamps_path, pitch, vcv_mode) 201 | except Exception as e: 202 | import traceback 203 | print("\n发生错误:") 204 | print(traceback.format_exc()) 205 | print("请联系开发者检查错误") 206 | finally: 207 | input('输入任意键退出') -------------------------------------------------------------------------------- /oto/oto_check.py: -------------------------------------------------------------------------------- 1 | import re 2 | from oto import oto_rw 3 | def cvvc_presamp_read(presamps_path): 4 | V = [] 5 | C = [] 6 | VC = [] 7 | VV = [] 8 | CV=[] 9 | CV_C=[] 10 | CV_V=[] 11 | with open(presamps_path, 'r',encoding='utf-8') as file: 12 | ini_text = file.read() 13 | # 提取 [VOWEL] 部分 14 | # 提取 [VOWEL] 部分 15 | vowel_match = re.search(r'\[VOWEL\](.*?)\[', ini_text, re.DOTALL) 16 | vowels = vowel_match.group(1).strip() 17 | for vowel in vowels.split('\n'): 18 | V.append(vowel.split('=')[0]) 19 | for CV1 in vowel.split('=')[2].split(','): 20 | if CV1 != '': 21 | CV.append(CV1) 22 | # print(vowels) 23 | # 提取 [CONSONANT] 部分 24 | consonant_match = re.search(r'\[CONSONANT\](.*?)\[', ini_text, re.DOTALL) 25 | if consonant_match != None: 26 | consonants = consonant_match.group(1).strip() 27 | # print(consonants) 28 | for consonant in consonants.split('\n'): 29 | C.append(consonant.split('=')[0]) 30 | for CV2 in consonant.split('=')[1].split(','): 31 | if CV2 != '': 32 | CV_C.append(CV2) 33 | 34 | V = set(V) 35 | C = set(C) 36 | for V1 in V: 37 | for C1 in C: 38 | VC.append(V1+' '+C1) 39 | VC = set(VC) 40 | CV = set(CV) 41 | CV_C = set(CV_C) 42 | CV_V = CV - CV_C 43 | for V1 in V: 44 | for C1 in CV_V: 45 | VV.append(V1+' '+C1) 46 | VV = set(VV) 47 | # CVVC = VC | CV 48 | # print(V,C,VV) 49 | return V,C,CV,VC,VV 50 | 51 | def oto_read(file_path): 52 | oto_data=[] 53 | encodings = ['shift-jis','utf-8', 'gbk'] 54 | for encoding in encodings: 55 | try: 56 | print(f'正在尝试使用 {encoding} 编码读取文件。') 57 | with open(file_path, 'r',encoding=encoding) as f: 58 | for line in f: 59 | line = line.strip() 60 | parts = line.split('=') 61 | # print(parts) 62 | parts2 = parts[1].split(',') 63 | # print(parts2) 64 | oto_data.append([parts[0]] + [parts2[0]] + [int(round(float(num_str))) for num_str in parts2[1:]]) 65 | # wav+别名+四舍五入后的数值 66 | print(f'成功使用 {encoding} 编码读取文件。') 67 | break 68 | except UnicodeDecodeError: 69 | continue 70 | else: 71 | print('无法使用尝试的编码读取oto文件,请检查文件编码。') 72 | input('按任意键退出') 73 | quit() 74 | print(f'oto文件解析成功:{file_path}') 75 | return oto_data 76 | 77 | def run(oto_path,presamps_path,pitch,vcv_mode): 78 | phone_name = [] 79 | if vcv_mode == '0': 80 | print('音源类型:CVVC') 81 | V_C = cvvc_presamp_read(presamps_path) 82 | oto_data = oto_read(oto_path) 83 | for byname in oto_data: 84 | phone_name.append(byname[1].replace(pitch, '')) 85 | print('缺少的CV音素:', end='') 86 | for phone in V_C[2]: 87 | if phone not in phone_name: 88 | print(phone,end=',') 89 | print('\n缺少的VC音素:', end='') 90 | for phone in V_C[3]: 91 | if phone not in phone_name: 92 | print(phone,end=',') 93 | print('\n缺少的VV音素:', end='') 94 | for phone in V_C[4]: 95 | if phone not in phone_name: 96 | print(phone,end=',') 97 | print('\n缺少的- CV音素(可忽略):', end='') 98 | CV = {'- ' + c for c in V_C[2]} 99 | for phone in CV: 100 | if phone not in phone_name: 101 | print(phone,end=',') 102 | elif vcv_mode == '1': 103 | phone=[] 104 | print('音源类型:VCV') 105 | V_C = cvvc_presamp_read(presamps_path) 106 | oto_data = oto_rw.oto_read(oto_path) 107 | for byname in oto_data: 108 | phone_name.append(byname[1].replace(pitch, '')) 109 | print('缺少的VCV音素:', end='') 110 | #V,C,CV,VC,VV 111 | for phone0 in V_C[0]: 112 | for phone2 in V_C[2]: 113 | phone.append(phone0+' '+phone2) 114 | for phone3 in phone: 115 | if phone3 not in phone_name: 116 | print(phone3, end=',') 117 | print('\n缺少的- CV音素:', end='') 118 | CV = {'- ' + c for c in V_C[2]} 119 | for phone in CV: 120 | if phone not in phone_name: 121 | print(phone, end=',') 122 | elif vcv_mode == '2': 123 | print('音源类型:CVV或CVVR') 124 | V_C = cvvc_presamp_read(presamps_path) 125 | oto_data = oto_rw.oto_read(oto_path) 126 | for byname in oto_data: 127 | phone_name.append(byname[1].replace(pitch, '')) 128 | print('缺少的CV音素:', end='') 129 | for phone in V_C[2]: 130 | if phone not in phone_name: 131 | print(phone, end=',') 132 | print('\n缺少的VR音素:', end='') 133 | for phone in V_C[3]: 134 | if phone not in phone_name: 135 | print(phone, end=',') 136 | print('\n缺少的_V音素:', end='') 137 | _V = {'_' + c for c in V_C[0]} 138 | for phone in _V: 139 | if phone not in phone_name: 140 | print(phone, end=',') 141 | 142 | print() 143 | 144 | 145 | if __name__ == '__main__': 146 | print('CVVC') 147 | run('E:\OpenUtau\Singers\XIABAI_new_CHN_CVVC_F3_autooto\F3\oto.ini','E:\OpenUtau\Singers\XIABAI_new_CHN_CVVC_F3_autooto\presamp.ini',' F3','0') 148 | print('VCV') 149 | run('E:\OpenUtau\Singers\空气音中文VCV_自动oto测试\VCV\oto.ini', '../presamp/樗儿式中文VCV-presamp.ini', '', '1') 150 | print('VCV') 151 | run('E:\OpenUtau\Singers\TNOT-Nottthat_VCV-TNOT-日语-VCV\VCV\oto.ini', '../presamp/jp-hira-presamp.ini', '', '1') 152 | print('CVVR') 153 | run('E:\OpenUtau\Singers\Weiyin3.0\combined\oto.ini', '../presamp/CVR中文-presamp.ini', '', '2') -------------------------------------------------------------------------------- /oto/oto_rw.py: -------------------------------------------------------------------------------- 1 | import os 2 | import wave 3 | import time 4 | #oto格式 5 | #文件名=别名,左边界,固定,右边界(负值),预发声,交叉 6 | 7 | # 定义颜色代码 8 | BLACK = '\033[30m' 9 | RED = '\033[31m' 10 | GREEN = '\033[32m' 11 | YELLOW = '\033[33m' 12 | BLUE = '\033[34m' 13 | MAGENTA = '\033[35m' 14 | CYAN = '\033[36m' 15 | WHITE = '\033[37m' 16 | RESET = '\033[0m' # 用于恢复默认颜色 17 | 18 | #文件存在验证 19 | def check_file(file_path): 20 | if os.path.exists(file_path): 21 | print(f'{GREEN}文件存在:{file_path}{RESET}') 22 | return False 23 | else: 24 | input(f'{RED}文件不存在:{file_path}{RESET}') 25 | return True 26 | 27 | # 此函数用于检查文件是否存在,如果存在则添加数字后缀以生成唯一的文件路径。 28 | def oto_path(file_path): 29 | new_file_path=file_path.strip(".ini") 30 | for i in range (1,100): 31 | if not os.path.exists(file_path): 32 | return file_path 33 | file_path = f"{new_file_path}_{i:02d}.ini" 34 | # print(file_path) 35 | 36 | #预发声转为负数 37 | def oto_check(file_path,oto_data): 38 | file_path=file_path.strip("oto.ini") 39 | new_oto_data=[] 40 | for oto in oto_data: 41 | if int(oto[4]) >0: 42 | with wave.open(file_path+oto[0], 'rb') as f: 43 | duration = f.getnframes() / f.getframerate() *1000 44 | oto[4] =int(duration - int(oto[2]) - int(oto[4]))*-1 45 | if oto[4]>0: 46 | print(f'{RED}右边界错误:{oto}wav:{duration}s{RESET}') 47 | new_oto_data.append(oto) 48 | return new_oto_data 49 | 50 | # 解析 oto 文件 51 | def oto_read(file_path): 52 | if check_file(file_path):return False 53 | oto_data=[] 54 | encodings = ['utf-8', 'shift-jis','gbk'] 55 | for encoding in encodings: 56 | try: 57 | with open(file_path, 'r', encoding=encoding) as f: 58 | for line in f: 59 | line = line.strip() 60 | parts = line.split('=') 61 | # print(parts) 62 | parts2 = parts[1].split(',') 63 | # print(parts2) 64 | oto_data.append([parts[0]] + [parts2[0]] + [int(round(float(num_str))) for num_str in parts2[1:]]) 65 | # wav+别名+四舍五入后的数值 66 | print(f'成功使用 {encoding} 编码读取文件。') 67 | break 68 | except UnicodeDecodeError: 69 | continue 70 | else: 71 | print('无法使用尝试的编码读取oto文件,请检查文件编码。') 72 | input('按任意键退出') 73 | quit() 74 | print(f'{GREEN}oto文件解析成功:{file_path}{RESET}') 75 | return oto_check(file_path,oto_data) 76 | #没写完 77 | def oto_repeat(oto_data,repeat): 78 | phone_count = {} 79 | new_oto_data = [] 80 | for oto in oto_data: 81 | phone = oto[1] 82 | if phone not in phone_count: 83 | phone_count[phone] = 1 84 | new_oto_data.append(oto) 85 | else: 86 | count = phone_count[phone] 87 | if count < repeat: 88 | oto[1] = f"{phone}_{count}" 89 | new_oto_data.append(oto) 90 | phone_count[phone] += 1 91 | # print(count) 92 | return new_oto_data 93 | 94 | #oto数值偏移函数 95 | def oto_offset(oto_data,offset): 96 | new_oto_data = [] 97 | oto_sum=[] 98 | # print(oto_data) 99 | # 遍历 oto_data 列表中的每个元素 100 | for oto in oto_data: 101 | # 0文件名=1别名,2左边界,3固定,4右边界(负值),5预发声,6交叉 102 | oto_sum=[oto[2] + offset[0],oto[3] + offset[1],oto[4]*-1 + offset[2],oto[5] + offset[3],oto[6] + offset[4]] 103 | 104 | if oto_sum[0] >= 0:oto[2] = oto_sum[0] # 左 105 | else:print(f'{oto[1]}修改后的左边界为{oto_sum[0]},跳过偏移操作') 106 | if oto_sum[4] >= 0:oto[6] = oto_sum[4]#交叉 107 | else: 108 | oto[6] = 20 109 | print(f'{oto[1]}错误的交叉:{oto_sum[4]},交叉设为20') 110 | if oto_sum[3] >= 0:oto[5] = oto_sum[3] 111 | else:print(f'{oto[1]}错误的预发声:{oto[5] + offset[0]},跳过偏移操作') 112 | if oto_sum[1] >= oto[5]:oto[3] = oto_sum[1] 113 | else: 114 | oto[3] = oto[5] 115 | print(f'{oto[1]}错误的固定:{oto[3] + offset[1]},设为{oto[5]}') 116 | if oto_sum[2] >= oto[3]:oto[4] = oto_sum[2]*-1 117 | else: 118 | oto[4] = (oto[3]+10)*-1 119 | print(f'{oto[1]}错误的右边界:{oto_sum[2]},设为{oto[4]}') 120 | 121 | new_oto_data.append(oto) 122 | return new_oto_data 123 | 124 | 125 | def oto_write(file_path,oto_data,pitch,cover): 126 | #写入 oto 文件 127 | if cover=='y' or cover=='Y': 128 | print(f'{GREEN}覆盖原文件:{file_path}{RESET}') 129 | new_file_path = file_path 130 | else: 131 | print(f'{GREEN}不覆盖原文件:{file_path}{RESET}') 132 | new_file_path=oto_path(file_path) 133 | encodings = ['shift-jis', 'utf-8'] 134 | for encoding in encodings: 135 | try: 136 | with open(new_file_path, 'w', encoding=encoding) as f: 137 | for oto in oto_data: 138 | f.write(f'{oto[0]}={oto[1]}{pitch},{oto[2]},{oto[3]},{oto[4]},{oto[5]},{oto[6]}\n') 139 | print(f'{GREEN}新oto以 {encoding} 编码写入成功:{new_file_path}{RESET}') 140 | break 141 | except UnicodeEncodeError: 142 | if encoding == 'shift-jis': 143 | print(f'{YELLOW}无法使用 shift-jis 编码写入,尝试使用 utf-8 编码...{RESET}') 144 | else: 145 | print(f'{RED}无法使用 utf-8 编码写入,请检查数据。{RESET}') 146 | else: 147 | print(f'{RED}所有尝试的编码都失败,无法写入文件。{RESET}') 148 | 149 | def run(oto_path): 150 | oto=oto_read(oto_path) 151 | oto_write(oto_path,oto) 152 | 153 | if __name__ == '__main__': 154 | start_time = time.time() 155 | 156 | path = "G:\编程\\UU2VV\E3 - 副本\oto.ini" 157 | oto=oto_read(path) 158 | oto_write("G:\编程\\UU2VV\F3\oto.ini",oto) 159 | 160 | end_time = time.time() 161 | elapsed_time = end_time - start_time 162 | print(f"代码运行耗时: {elapsed_time*1000} 毫秒") -------------------------------------------------------------------------------- /presamp/CVR中文-presamp.ini: -------------------------------------------------------------------------------- 1 | [VERSION] 2 | 1.2 3 | [VOWEL] 4 | a=a=a,ba,pa,ma,fa,da,ta,na,la,ga,ka,ha,zha,cha,sha,za,ca,sa,ya,wa=100 5 | ua=ua=gua,kua,hua,zhua,shua=100 6 | ia=ia=lia,jia,qia,xia,dia=100 7 | ang=ang=ang,bang,pang,mang,fang,dang,tang,nang,lang,gang,kang,hang,zhang,chang,shang,rang,zang,cang,sang,yang,wang=100 8 | iang=iang=liang,jiang,qiang,xiang,niang=100 9 | uang=uang=guang,kuang,huang,zhuang,chuang,shuang=100 10 | ei=ei=ei,bei,pei,mei,fei,dei,tei,nei,lei,gei,kei,hei,zhei,shei,zei,wei=100 11 | ui=ui=dui,tui,gui,kui,hui,zhui,chui,shui,rui,zui,cui,sui=100 12 | ai=ai=ai,bai,pai,mai,dai,tai,nai,lai,gai,kai,hai,zhai,chai,shai,zai,cai,sai,wai=100 13 | uai=uai=guai,kuai,huai,zhuai,chuai,shuai=100 14 | ir=ir=zhi,chi,shi,ri=100 15 | ong=ong=ong,bong,pong,mong,fong,dong,tong,nong,long,gong,kong,hong,wong,zhong,chong,rong,zong,cong,song,yong=100 16 | iong=iong=jiong,qiong,xiong=100 17 | ao=ao=ao,bao,pao,mao,dao,tao,nao,lao,gao,kao,hao,zhao,chao,shao,rao,zao,cao,sao,yao=100 18 | iao=iao=biao,piao,miao,diao,tiao,niao,liao,jiao,qiao,xiao=100 19 | an=an=an,ban,pan,man,fan,dan,tan,nan,lan,gan,kan,han,zhan,chan,shan,ran,zan,can,san,wan=100 20 | uan=uan=duan,tuan,nuan,luan,guan,kuan,huan,zhuan,chuan,shuan,ruan,zuan,cuan,suan=100 21 | un=un=dun,tun,lun,gun,kun,hun,zhun,chun,shun,run,zun,cun,sun=100 22 | en=en=en,ben,pen,men,fen,ten,den,nen,gen,ken,hen,zhen,chen,shen,ren,zen,cen,sen,wen=100 23 | in=in=yin,bin,pin,min,din,tin,nin,lin,jin,qin,xin=100 24 | ing=ing=ying,bing,ping,ming,ding,ting,ning,ling,jing,qing,xing=100 25 | er=er=er=100 26 | i0=i0=zi,ci,si=100 27 | vn=vn=yun,jun,qun,xun=100 28 | e=e=e,me,de,te,ne,le,ge,ke,he,zhe,che,she,re,ze,ce,se=100 29 | i=i=yi,i,bi,pi,mi,di,ti,ni,li,ji,qi,xi=100 30 | o=o=o,bo,po,mo,fo=100 31 | uo=uo=wo,duo,tuo,nuo,luo,guo,kuo,huo,zhuo,chuo,shuo,ruo,zuo,cuo,suo=100 32 | u=u=u,bu,pu,mu,fu,du,tu,nu,lu,gu,ku,hu,zhu,chu,shu,ru,zu,cu,su,wu=100 33 | v=v=yu,nv,lv,ju,qu,xu=100 34 | ou=ou=ou,pou,mou,fou,dou,tou,nou,lou,gou,kou,hou,zhou,chou,shou,rou,zou,cou,sou,you=100 35 | iu=iu=miu,diu,niu,liu,jiu,qiu,xiu=100 36 | e0=e0=ye,bie,pie,mie,die,tie,nie,lie,jie,qie,xie=100 37 | ve=ve=yue,nue,lue,jue,que,xue=100 38 | eng=eng=eng,deng,teng,neng,leng,geng,keng,heng,zheng,cheng,sheng,reng,zeng,ceng,seng,beng,peng,meng,feng,weng=100 39 | en2=en0=yan,bian,pian,mian,dian,tian,nian,lian,jian,qian,xian=100 40 | van=van=yuan,juan,quan,xuan=100 41 | [CONSONANT] 42 | R=a,ba,pa,ma,fa,da,ta,na,la,ga,ka,ha,zha,cha,sha,za,ca,sa,ya,wa,gua,kua,hua,zhua,shua,lia,jia,qia,xia,dia,ang,bang,pang,mang,fang,dang,tang,nang,lang,gang,kang,hang,zhang,chang,shang,rang,zang,cang,sang,yang,wang,liang,jiang,qiang,xiang,niang,guang,kuang,huang,zhuang,chuang,shuang,ei,bei,pei,mei,fei,dei,tei,nei,lei,gei,kei,hei,zhei,shei,zei,wei,dui,tui,gui,kui,hui,zhui,chui,shui,rui,zui,cui,sui,ai,bai,pai,mai,dai,tai,nai,lai,gai,kai,hai,zhai,chai,shai,zai,cai,sai,wai,guai,kuai,huai,zhuai,chuai,shuai,zhi,chi,shi,ri,ong,bong,pong,mong,fong,dong,tong,nong,long,gong,kong,hong,wong,zhong,chong,rong,zong,cong,song,yong,jiong,qiong,xiong,ao,bao,pao,mao,dao,tao,nao,lao,gao,kao,hao,zhao,chao,shao,rao,zao,cao,sao,yao,biao,piao,miao,diao,tiao,niao,liao,jiao,qiao,xiao,an,ban,pan,man,fan,dan,tan,nan,lan,gan,kan,han,zhan,chan,shan,ran,zan,can,san,wan,duan,tuan,nuan,luan,guan,kuan,huan,zhuan,chuan,shuan,ruan,zuan,cuan,suan,dun,tun,lun,gun,kun,hun,zhun,chun,shun,run,zun,cun,sun,en,ben,pen,men,fen,ten,den,nen,gen,ken,hen,zhen,chen,shen,ren,zen,cen,sen,wen,yin,bin,pin,min,din,tin,nin,lin,jin,qin,xin,ying,bing,ping,ming,ding,ting,ning,ling,jing,qing,xing,er,zi,ci,si,yun,jun,qun,xun,e,me,de,te,ne,le,ge,ke,he,zhe,che,she,re,ze,ce,se,yi,i,bi,pi,mi,di,ti,ni,li,ji,qi,xi,o,bo,po,mo,fo,wo,duo,tuo,nuo,luo,guo,kuo,huo,zhuo,chuo,shuo,ruo,zuo,cuo,suo,u,bu,pu,mu,fu,du,tu,nu,lu,gu,ku,hu,zhu,chu,shu,ru,zu,cu,su,wu,yu,nv,lv,ju,qu,xu,ou,pou,mou,fou,dou,tou,nou,lou,gou,kou,hou,zhou,chou,shou,rou,zou,cou,sou,you,miu,diu,niu,liu,jiu,qiu,xiu,ye,bie,pie,mie,die,tie,nie,lie,jie,qie,xie,yue,nue,lue,jue,que,xue,eng,deng,teng,neng,leng,geng,keng,heng,zheng,cheng,sheng,reng,zeng,ceng,seng,beng,peng,meng,feng,weng,yan,bian,pian,mian,dian,tian,nian,lian,jian,qian,xian,yuan,juan,quan,xuan=0 43 | [PRIORITY] 44 | k,g,t,d,b,p 45 | [REPLACE] 46 | a=a 47 | [ENDTYPE] 48 | %v% R 49 | [ENDFLAG] 50 | 1 -------------------------------------------------------------------------------- /presamp/arpasing-presamp.ini: -------------------------------------------------------------------------------- 1 | [VERSION] 2 | 1.2 3 | [VOWEL] 4 | aa=aa=aa=100 5 | ae=ae=ae=100 6 | ah=ah=ah=100 7 | ao=ao=ao=100 8 | aw=aw=aw=100 9 | ax=ax=ax=100 10 | ay=ay=ay=100 11 | b=b=b=100 12 | ch=ch=ch=100 13 | d=d=d=100 14 | dh=dh=dh=100 15 | dr=dr=dr=100 16 | dx=dx=dx=100 17 | eh=eh=eh=100 18 | er=er=er=100 19 | ey=ey=ey=100 20 | f=f=f=100 21 | g=g=g=100 22 | hh=hh=hh=100 23 | ih=ih=ih=100 24 | iy=iy=iy=100 25 | jh=jh=jh=100 26 | k=k=k=100 27 | l=l=l=100 28 | m=m=m=100 29 | n=n=n=100 30 | ng=ng=ng=100 31 | ow=ow=ow=100 32 | oy=oy=oy=100 33 | p=p=p=100 34 | q=q=q=100 35 | r=r=r=100 36 | s=s=s=100 37 | sh=sh=sh=100 38 | t=t=t=100 39 | th=th=th=100 40 | tr=tr=tr=100 41 | uh=uh=uh=100 42 | uw=uw=uw=100 43 | v=v=v=100 44 | vf=vf=vf=100 45 | w=w=w=100 46 | y=y=y=100 47 | z=z=z=100 48 | zh=zh=zh=100 49 | i=i=i=100 50 | N=N=N=100 51 | M=M=M=100 52 | oh=oh=oh=100 53 | [PRIORITY] 54 | k,g,t,d,b,p 55 | [REPLACE] 56 | a=a 57 | [ENDTYPE] 58 | %v%R 59 | [ENDFLAG] 60 | 1 -------------------------------------------------------------------------------- /presamp/jp-hira-presamp.ini: -------------------------------------------------------------------------------- 1 | [VERSION] 2 | 1.7 3 | [VOWEL] 4 | n=n=ん=0 5 | a=a=あ,きゃ,ぎゃ,くぁ,ぐぁ,しゃ,じゃ,ちゃ,つぁ,にゃ,ひゃ,びゃ,ぴゃ,ふぁ,ぶぁ,みゃ,りゃ,ヴぁ,ガ,か,が,さ,ざ,た,だ,な,は,ば,ぱ,ま,や,ら,わ=0 6 | i=i=い,うぃ,くぃ,ぐぃ,すぃ,ずぃ,つぃ,てぃ,でぃ,ふぃ,ぶぃ,ヴぃ,ギ,き,ぎ,し,じ,ち,に,ひ,び,ぴ,み,り=0 7 | u=u=う,きゅ,ぎゅ,しゅ,じゅ,ちゅ,てゅ,でゅ,とぅ,どぅ,にゅ,ひゅ,びゅ,ぴゅ,みゅ,りゅ,ヴ,グ,く,ぐ,す,ず,つ,ぬ,ふ,ぶ,ぷ,む,ゆ,る=0 8 | e=e=え,いぇ,うぇ,きぇ,ぎぇ,くぇ,ぐぇ,しぇ,じぇ,ちぇ,つぇ,にぇ,ひぇ,びぇ,ぴぇ,ふぇ,ぶぇ,みぇ,りぇ,ヴぇ,ゲ,け,げ,せ,ぜ,て,で,ね,へ,べ,ぺ,め,れ=0 9 | o=o=お,を,うぉ,きょ,ぎょ,くぉ,ぐぉ,しょ,じょ,ちょ,つぉ,にょ,ひょ,びょ,ぴょ,ふぉ,ぶぉ,みょ,りょ,ヴぉ,ゴ,こ,ご,そ,ぞ,と,ど,の,ほ,ぼ,ぽ,も,よ,ろ=0 10 | [CONSONANT] 11 | y=いぇ,や,ゆ,よ=0 12 | w=うぇ,うぃ,を,うぉ,わ=0 13 | ky=きゃ,きゅ,きぇ,きょ=0 14 | gy=ぎゃ,ぎゅ,ぎぇ,ぎょ=0 15 | kw=くぁ,くぃ,くぇ,くぉ=0 16 | gw=ぐぁ,ぐぃ,ぐぇ,ぐぉ=0 17 | sh=しゃ,しゅ,しぇ,しょ,し=0 18 | j=じゃ,じぇ,じゅ,じょ,じ=0 19 | s=すぃ,さ,す,せ,そ=0 20 | z=ずぃ,ざ,ず,ぜ,ぞ=0 21 | ch=ちゃ,ちゅ,ちぇ,ちょ,ち=0 22 | ts=つぁ,つぃ,つぇ,つぉ,つ=0 23 | t=てぃ,とぅ,た,て,と=0 24 | ty=てゅ=0 25 | d=でぃ,どぅ,だ,で,ど=0 26 | dy=でゅ=0 27 | ny=にゃ,にぇ,にゅ,にょ=0 28 | hy=ひゃ,ひゅ,ひぇ,ひょ=0 29 | by=びゃ,びゅ,びぇ,びょ=0 30 | py=ぴゃ,ぴゅ,ぴぇ,ぴょ=0 31 | f=ふぁ,ふぃ,ふぇ,ふぉ=0 32 | bw=ぶぁ,ぶぃ,ぶぇ,ぶぉ=0 33 | my=みゃ,みゅ,みぇ,みょ=0 34 | ry=りゃ,りゅ,りぇ,りょ=0 35 | v=ヴぁ,ヴぃ,ヴぇ,ヴぉ,ヴ=0 36 | ng=ガ,ギ,グ,ゲ,ゴ=0 37 | k=か,き,く,け,こ=0 38 | g=が,ぎ,ぐ,げ,ご=0 39 | n=な,に,ぬ,ね,の=0 40 | h=は,ひ,ふ,へ,ほ=0 41 | b=ば,び,ぶ,べ,ぼ=0 42 | p=ぱ,ぴ,ぷ,ぺ,ぽ=0 43 | m=ま,み,む,め,も=0 44 | r=ら,り,る,れ,ろ=0 45 | [ENDFLAG] 46 | 1 47 | -------------------------------------------------------------------------------- /presamp/jp-roma-hira-presamp.ini: -------------------------------------------------------------------------------- 1 | [VERSION] 2 | 1.7 3 | [VOWEL] 4 | n=n=N,ん=0 5 | a=a=a,kya,gya,kwa,gwa,sha,ja,cha,tsa,nya,hya,bya,pya,fa,bwa,mya,rya,va,nga,ka,ga,sa,za,ta,da,na,ha,ba,pa,ma,ya,ra,wa,あ,きゃ,ぎゃ,くぁ,ぐぁ,しゃ,じゃ,ちゃ,つぁ,にゃ,ひゃ,びゃ,ぴゃ,ふぁ,ぶぁ,みゃ,りゃ,ヴぁ,ガ,か,が,さ,ざ,た,だ,な,は,ば,ぱ,ま,や,ら,わ=0 6 | i=i=i,wi,kwi,gwi,si,zi,tsi,ti,di,fi,bwi,vi,ngi,ki,gi,shi,ji,chi,ni,hi,bi,pi,mi,ri,い,うぃ,くぃ,ぐぃ,すぃ,ずぃ,つぃ,てぃ,でぃ,ふぃ,ぶぃ,ヴぃ,ギ,き,ぎ,し,じ,ち,に,ひ,び,ぴ,み,り=0 7 | u=u=u,kyu,gyu,shu,ju,chu,tyu,dyu,tu,du,nyu,hyu,byu,pyu,myu,ryu,vu,ngu,ku,gu,su,zu,tsu,nu,fu,hu,bu,pu,mu,yu,ru,う,きゅ,ぎゅ,しゅ,じゅ,ちゅ,てゅ,でゅ,とぅ,どぅ,にゅ,ひゅ,びゅ,ぴゅ,みゅ,りゅ,ヴ,グ,く,ぐ,す,ず,つ,ぬ,fu,ふ,ぶ,ぷ,む,ゆ,る=0 8 | e=e=e,ye,we,kye,gye,kwe,gwe,she,je,che,tse,nye,hye,bye,pye,fe,bwe,mye,rye,ve,nge,ke,ge,se,ze,te,de,ne,he,be,pe,me,re,え,いぇ,うぇ,きぇ,ぎぇ,くぇ,ぐぇ,しぇ,じぇ,ちぇ,つぇ,にぇ,ひぇ,びぇ,ぴぇ,ふぇ,ぶぇ,みぇ,りぇ,ヴぇ,ゲ,け,げ,せ,ぜ,て,で,ね,へ,べ,ぺ,め,れ=0 9 | o=o=o,wo,kyo,gyo,kwo,gwo,sho,jo,cho,tso,nyo,hyo,byo,pyo,fo,bwo,myo,ryo,vo,ngo,ko,go,so,zo,to,do,no,ho,bo,po,mo,yo,ro,お,を,うぉ,きょ,ぎょ,くぉ,ぐぉ,しょ,じょ,ちょ,つぉ,にょ,ひょ,びょ,ぴょ,ふぉ,ぶぉ,みょ,りょ,ヴぉ,ゴ,こ,ご,そ,ぞ,と,ど,の,ほ,ぼ,ぽ,も,よ,ろ=0 10 | [CONSONANT] 11 | y=ye,ya,yu,yo,いぇ,や,ゆ,よ=0 12 | w=we,wi,wo,wa,うぇ,うぃ,を,うぉ,わ=0 13 | ky=kya,kyu,kye,kyo,きゃ,きゅ,きぇ,きょ=0 14 | gy=gya,gyu,gye,gyo,ぎゃ,ぎゅ,ぎぇ,ぎょ=0 15 | kw=kwa,kwi,kwe,kwo,くぁ,くぃ,くぇ,くぉ=0 16 | gw=gwa,gwi,gwe,gwo,ぐぁ,ぐぃ,ぐぇ,ぐぉ=0 17 | sh=sha,shu,she,sho,shi,しゃ,しゅ,しぇ,しょ,し=0 18 | j=ja,je,ju,jo,ji,じゃ,じぇ,じゅ,じょ,じ=0 19 | s=si,sa,su,se,so,すぃ,さ,す,せ,そ=0 20 | z=zi,za,zu,ze,zo,ずぃ,ざ,ず,ぜ,ぞ=0 21 | ch=cha,chu,che,cho,chi,ちゃ,ちゅ,ちぇ,ちょ,ち=0 22 | ts=tsa,tsi,tse,tso,tsu,つぁ,つぃ,つぇ,つぉ,つ=0 23 | t=ti,tu,ta,te,to,てぃ,とぅ,た,て,と=0 24 | ty=tyu,てゅ=0 25 | d=di,du,da,de,do,でぃ,どぅ,だ,で,ど=0 26 | dy=dyu,でゅ=0 27 | ny=nya,nye,nyu,nyo,にゃ,にぇ,にゅ,にょ=0 28 | hy=hya,hyu,hye,hyo,ひゃ,ひゅ,ひぇ,ひょ=0 29 | by=bya,byu,bye,byo,びゃ,びゅ,びぇ,びょ=0 30 | py=pya,pyu,pye,pyo,ぴゃ,ぴゅ,ぴぇ,ぴょ=0 31 | f=fa,fi,fe,fo,ふぁ,ふぃ,ふぇ,ふぉ=0 32 | bw=bwa,bwi,bwe,bwo,ぶぁ,ぶぃ,ぶぇ,ぶぉ=0 33 | my=mya,myu,mye,myo,みゃ,みゅ,みぇ,みょ=0 34 | ry=rya,ryu,rye,ryo,りゃ,りゅ,りぇ,りょ=0 35 | v=va,vi,ve,vo,vu,ヴぁ,ヴぃ,ヴぇ,ヴぉ,ヴ=0 36 | ng=nga,ngi,ngu,nge,ngo,ガ,ギ,グ,ゲ,ゴ=0 37 | k=ka,ki,ku,ke,ko,か,き,く,け,こ=0 38 | g=ga,gi,gu,ge,go,が,ぎ,ぐ,げ,ご=0 39 | n=na,ni,nu,ne,no,な,に,ぬ,ね,の=0 40 | h=ha,hi,fu,hu,he,ho,は,ひ,ふ,へ,ほ=0 41 | b=ba,bi,bu,be,bo,ば,び,ぶ,べ,ぼ=0 42 | p=pa,pi,pu,pe,po,ぱ,ぴ,ぷ,ぺ,ぽ=0 43 | m=ma,mi,mu,me,mo,ま,み,む,め,も=0 44 | r=ra,ri,ru,re,ro,ら,り,る,れ,ろ=0 45 | [ENDFLAG] 46 | 1 47 | -------------------------------------------------------------------------------- /presamp/jp-romaji-presamp.ini: -------------------------------------------------------------------------------- 1 | [VERSION] 2 | 1.7 3 | [VOWEL] 4 | n=n=N=100 5 | a=a=a,kya,gya,kwa,gwa,sha,ja,cha,tsa,nya,hya,bya,pya,fa,bwa,mya,rya,va,nga,ka,ga,sa,za,ta,da,na,ha,ba,pa,ma,ya,ra,wa=100 6 | i=i=i,wi,kwi,gwi,si,zi,tsi,ti,di,fi,bwi,vi,ngi,ki,gi,shi,ji,chi,ni,hi,bi,pi,mi,ri=100 7 | u=u=u,kyu,gyu,shu,ju,chu,tyu,dyu,tu,du,nyu,hyu,byu,pyu,myu,ryu,vu,ngu,ku,gu,su,zu,tsu,nu,fu,hu,bu,pu,mu,yu,ru=100 8 | e=e=e,ye,we,kye,gye,kwe,gwe,she,je,che,tse,nye,hye,bye,pye,fe,bwe,mye,rye,ve,nge,ke,ge,se,ze,te,de,ne,he,be,pe,me,re=100 9 | o=o=o,wo,kyo,gyo,kwo,gwo,sho,jo,cho,tso,nyo,hyo,byo,pyo,fo,bwo,myo,ryo,vo,ngo,ko,go,so,zo,to,do,no,ho,bo,po,mo,yo,ro=100 10 | [CONSONANT] 11 | y=ye,ya,yu,yo=0 12 | w=we,wi,wo,wa=0 13 | ky=kya,kyu,kye,kyo=0 14 | gy=gya,gyu,gye,gyo=0 15 | kw=kwa,kwi,kwe,kwo=0 16 | gw=gwa,gwi,gwe,gwo=0 17 | sh=sha,shu,she,sho,shi=0 18 | j=ja,je,ju,jo,ji=0 19 | s=si,sa,su,se,so=0 20 | z=zi,za,zu,ze,zo=0 21 | ch=cha,chu,che,cho,chi=0 22 | ts=tsa,tsi,tse,tso,tsu=0 23 | t=ti,tu,ta,te,to=0 24 | ty=tyu=0 25 | d=di,du,da,de,do=0 26 | dy=dyu=0 27 | ny=nya,nye,nyu,nyo=0 28 | hy=hya,hyu,hye,hyo=0 29 | by=bya,byu,bye,byo=0 30 | py=pya,pyu,pye,pyo=0 31 | f=fa,fi,fe,fo=0 32 | bw=bwa,bwi,bwe,bwo=0 33 | my=mya,myu,mye,myo=0 34 | ry=rya,ryu,rye,ryo=0 35 | v=va,vi,ve,vo,vu=0 36 | ng=nga,ngi,ngu,nge,ngo=0 37 | k=ka,ki,ku,ke,ko=0 38 | g=ga,gi,gu,ge,go=0 39 | n=na,ni,nu,ne,no=0 40 | h=ha,hi,fu,hu,he,ho=0 41 | b=ba,bi,bu,be,bo=0 42 | p=pa,pi,pu,pe,po=0 43 | m=ma,mi,mu,me,mo=0 44 | r=ra,ri,ru,re,ro=0 45 | [ENDFLAG] 46 | 1 47 | -------------------------------------------------------------------------------- /presamp/risku中文CVVCpresamp.ini: -------------------------------------------------------------------------------- 1 | [VERSION] 2 | 1.6 3 | [VOWEL] 4 | a=a=a,ba,pa,ma,fa,da,ta,na,la,ga,ka,ha,zha,cha,sha,za,ca,sa,ya,lia,jia,qia,xia,wa,gua,kua,hua,zhua,shua,dia=100 5 | ai=ai=ai,bai,pai,mai,dai,tai,nai,lai,gai,kai,hai,zhai,chai,shai,zai,cai,sai,wai,guai,kuai,huai,zhuai,chuai,shuai=100 6 | an=an=an,ban,pan,man,fan,dan,tan,nan,lan,gan,kan,han,zhan,chan,shan,ran,zan,can,san,wan,duan,tuan,nuan,luan,guan,kuan,huan,zhuan,chuan,shuan,ruan,zuan,cuan,suan=100 7 | ang=ang=ang,bang,pang,mang,fang,dang,tang,nang,lang,gang,kang,hang,zhang,chang,shang,rang,zang,cang,sang,yang,liang,jiang,qiang,xiang,wang,guang,kuang,huang,zhuang,chuang,shuang,niang=100 8 | ao=ao=ao,bao,pao,mao,dao,tao,nao,lao,gao,kao,hao,zhao,chao,shao,rao,zao,cao,sao,yao,biao,piao,miao,diao,tiao,niao,liao,jiao,qiao,xiao=100 9 | e=e=e,me,de,te,ne,le,ge,ke,he,zhe,che,she,re,ze,ce,se=100 10 | ei=ei=ei,bei,pei,mei,fei,dei,tei,nei,lei,gei,kei,hei,zhei,shei,zei,wei,dui,tui,gui,kui,hui,zhui,chui,shui,rui,zui,cui,sui=100 11 | e0=e0=ye,bie,pie,mie,die,tie,nie,lie,jie,qie,xie,yue,nue,lue,jue,que,xue=100 12 | en=en=en,ben,pen,men,fen,nen,gen,ken,hen,zhen,chen,shen,ren,zen,cen,sen,wen,dun,tun,lun,gun,kun,hun,zhun,chun,shun,run,zun,cun,sun=100 13 | en0=en0=yan,bian,pian,mian,dian,tian,nian,lian,jian,qian,xian,yuan,juan,quan,xuan=100 14 | eng=eng=eng,beng,peng,meng,feng,deng,teng,neng,leng,geng,keng,heng,weng,zheng,cheng,sheng,reng,zeng,ceng,seng=100 15 | er=er=er=100 16 | o=o=o,bo,po,mo,fo,wo,duo,tuo,nuo,luo,guo,kuo,huo,zhuo,chuo,shuo,ruo,zuo,cuo,suo=100 17 | ong=ong=ong,dong,tong,nong,long,gong,kong,hong,zhong,chong,rong,zong,cong,song,yong,jiong,qiong,xiong=100 18 | ou=ou=ou,pou,mou,fou,dou,tou,lou,gou,kou,hou,zhou,chou,shou,rou,zou,cou,sou,you,miu,diu,niu,liu,jiu,qiu,xiu=100 19 | i=i=i,bi,pi,mi,di,ti,ni,li,ji,qi,xi,yi=100 20 | in=in=yin,bin,pin,min,nin,lin,jin,qin,xin=100 21 | ing=ing=ying,bing,ping,ming,ding,ting,ning,ling,jing,qing,xing=100 22 | i0=i0=zi,ci,si=100 23 | ir=ir=zhi,chi,shi,ri=100 24 | u=u=u,bu,pu,mu,fu,du,tu,nu,lu,gu,ku,hu,zhu,chu,shu,ru,zu,cu,su,wu=100 25 | v0=v=yu,nv,lv,ju,qu,xu=100 26 | vn=vn=yun,jun,qun,xun=100 27 | [CONSONANT] 28 | b=ba,bang,bao,biao,bai,ban,bo,ben,beng,bei,bie,bu,bi,bin,bing,bian=1 29 | p=pa,pang,pao,piao,pai,pan,po,pou,pen,peng,pei,pie,pu,pi,pin,ping,pian=1 30 | m=ma,mang,mao,mai,man,mo,mou,me,men,meng,mei,mu=0 31 | my=miao,miu,mie,mi,min,ming,mian=0 32 | f=fa,fang,fan,fo,fou,fen,feng,fei,fu=0 33 | d=da,dia,dang,dao,diao,dai,dan,duan,duo,dong,dou,diu,de,dun,deng,dei,dui,die,du,di,ding,dian=1 34 | t=ta,tang,tao,tiao,tai,tan,tuan,tuo,tong,tou,te,tun,teng,tei,tui,tie,tu,ti,ting,tian=1 35 | n=na,nang,nao,nai,nan,nuan,nuo,nong,ne,nen,neng,nei,nue,nu,nv=0 36 | ny=niang,niao,niu,nie,ni,nin,ning,nian=0 37 | l=la,lang,lao,lai,lan,luan,luo,long,lou,le,lun,leng,lei,lue,lu,lv=0 38 | ly=lia,liang,liao,liu,lie,li,lin,ling,lian=0 39 | g=ga,gua,gang,guang,gao,gai,guai,gan,guan,guo,gong,gou,ge,gen,gun,geng,gei,gui,gu=1 40 | k=ka,kua,kang,kuang,kao,kai,kuai,kan,kuan,kuo,kong,kou,ke,ken,kun,keng,kei,kui,ku=1 41 | h=ha,hang,hao,hai,han,hong,hou,he,hen,heng,hei=0 42 | hw=hua,huang,huai,huan,huo,hun,hui,hu=0 43 | jy=jia,jiang,jiao,jiong,jiu,jie,ji,jin,jing,jian=1 44 | jw=jue,ju,jun,juan=1 45 | qy=qia,qiang,qiao,qiong,qiu,qie,qi,qin,qing,qian=1 46 | qw=que,qu,qun,quan=1 47 | xy=xia,xiang,xiao,xiong,xiu,xie,xi,xin,xing,xian=0 48 | xw=xue,xu,xun,xuan=0 49 | z=za,zang,zao,zai,zan,zong,zou,ze,zen,zeng,zei,zi,zuan,zuo,zun,zui,zu=1 50 | c=ca,cang,cao,cai,can,cong,cou,ce,cen,ceng,ci,cuan,cuo,cun,cui,cu=1 51 | s=sa,sang,sao,sai,san,song,sou,se,sen,seng,si=1 52 | sw=suan,suo,sun,sui,su=0 53 | zh=zha,zhang,zhao,zhai,zhan,zhong,zhou,zhe,zhen,zheng,zhei,zhi,zhua,zhuang,zhuai,zhuan,zhuo,zhun,zhui,zhu=1 54 | ch=cha,chang,chao,chai,chan,chong,chou,che,chen,cheng,chi,chuang,chuai,chuan,chuo,chun,chui,chu=1 55 | sh=sha,shang,shao,shai,shan,shou,she,shen,sheng,shei,shi=1 56 | shw=shua,shuang,shuai,shuan,shuo,shun,shui,shu=0 57 | r=ran,rang,rao,rou,rong,re,ren,reng,ru,run,ruan,rui,ruo,ri=0 58 | y=ya,yang,yao,yong,you,ye,yi,yin,ying,yan=0 59 | w=wa,wang,wai,wan,wo,wen,weng,wei,wu=0 60 | v=yue,yu,yun,yuan=0 61 | [PRIORITY] 62 | k,g,t,d,b,p 63 | [REPLACE] 64 | a=a 65 | [ENDTYPE] 66 | %v% R 67 | [ENDFLAG] 68 | 1 69 | [MUSTVC] 70 | 1 71 | -------------------------------------------------------------------------------- /presamp/樗儿式中文VCV-presamp.ini: -------------------------------------------------------------------------------- 1 | [VERSION] 2 | 1.2 3 | [VOWEL] 4 | a=a=za,ca,sha,ta,lia,ka,cha,ga,kua,fa,hua,da,wa,pa,shua,zha,ma,ha,qia,jia,gua,zhua,xia,ba,dia,na,la,sa,ya,a=100 5 | ang=ang=nang,guang,cang,zhang,yang,niang,ang,dang,liang,shang,zhuang,kang,hang,tang,pang,chuang,huang,zang,wang,bang,jiang,gang,sang,kuang,rang,fang,chang,xiang,shuang,mang,qiang,lang=100 6 | ao=ao=gao,pao,yao,hao,diao,zao,qiao,shao,mao,cao,piao,xiao,zhao,tiao,lao,biao,sao,kao,nao,liao,dao,chao,jiao,miao,ao,tao,rao,niao,bao=100 7 | ai=ai=mai,lai,dai,pai,gai,chai,kuai,cai,bai,huai,shai,ai,chuai,tai,guai,zhuai,wai,hai,nai,shuai,zai,kai,zhai,sai=100 8 | an=an=duan,zan,shan,wan,ran,huan,guan,an,ruan,ban,chan,kuan,kan,tan,zhuan,han,can,nan,lan,dan,fan,pan,zhan,chuan,san,man,nuan,suan,shuan,zuan,luan,gan,tuan,cuan=100 9 | o=o=shuo,tuo,zhuo,ruo,bo,kuo,mo,fo,guo,duo,o,huo,suo,luo,zuo,po,cuo,wo,chuo,nuo=100 10 | ong=ong=qiong,ong,rong,tong,cong,xiong,dong,nong,jiong,yong,long,song,chong,gong,kong,hong,zhong,zong=100 11 | ou=ou=you,rou,qiu,dou,shou,diu,sou,mou,zou,niu,jiu,hou,miu,ou,kou,liu,cou,zhou,lou,chou,xiu,gou,fou,tou,pou=100 12 | e=e=se,le,che,ke,re,he,zhe,ge,she,ne,de,ce,ze,e,te,me=100 13 | en=en=cen,ken,ren,zhen,hen,sen,hun,zun,fen,pen,kun,zhun,lun,zen,sun,en,dun,nen,chen,ben,shun,run,shen,cun,tun,wen,chun,gen,gun,men=100 14 | eng=eng=teng,zeng,reng,weng,keng,seng,heng,geng,eng,cheng,sheng,neng,meng,zheng,beng,peng,leng,ceng,deng,feng=100 15 | ei=ei=sui,shei,nei,hei,pei,lei,zhei,tei,chui,tui,rui,zui,hui,zhui,mei,gui,kei,fei,gei,ei,wei,dei,cui,shui,zei,dui,bei,kui=100 16 | ie=ie=qie,nie,mie,ye,die,tie,lie,xie,pie,jie,bie=100 17 | ue=ue=nue,que,jue,yue,lue,xue=100 18 | u=u=wu,u,ru,ku,nu,lu,gu,bu,shu,zhu,chu,cu,pu,mu,zu,su,hu,tu,fu,du=100 19 | v=v=nv,lv,yu,qu,xu,ju=100 20 | vn=vn=jun,xun,qun,yun=100 21 | i=i=yi,i,bi,ti,ji,ni,xi,li,mi,pi,qi,di=100 22 | in=in=bin,jin,lin,qin,pin,yin,nin,min,xin=100 23 | ing=ing=jing,ding,ning,ting,qing,ping,xing,bing,ying,ming,ling=100 24 | ir=ir=ri,shi,zhi,chi=100 25 | iz=iz=si,zi,ci=100 26 | er=er=er=100 27 | ian=ian=quan,xuan,tian,pian,nian,yan,juan,bian,yuan,mian,dian,lian,qian,xian,jian=100 28 | [PRIORITY] 29 | k,g,t,d,b,p 30 | [REPLACE] 31 | a=a 32 | [ENDTYPE] 33 | %v%R 34 | [ENDFLAG] 35 | 1 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | #pip install -r requirements.txt 2 | 3 | pywebview 4 | gradio 5 | click 6 | einops 7 | h5py 8 | librosa 9 | lightning 10 | matplotlib 11 | numpy 12 | PyYAML 13 | tensorboard 14 | tensorboardX 15 | tqdm 16 | textgrid 17 | chardet 18 | numba 19 | torch 20 | torchaudio 21 | pandas 22 | transformers 23 | openai-whisper -------------------------------------------------------------------------------- /textgrid2json/TextGrid2ds_json.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | 5 | def textgrid_change(textgrid_content): 6 | lines = textgrid_content.split('\n') 7 | lines = [line.strip() for line in lines if line.strip()] 8 | start = 0 #起始位置 9 | sum_indices =[] #间隔位置 10 | phones_dict = {} 11 | #获取长度 12 | long = [float(match.group(1)) for match in re.finditer(r'(?:xmin|xmax) = (\d+(?:\.\d+)?)', textgrid_content)][:2] 13 | for i in range(len(lines)): 14 | if lines[i] == 'name = "phones"': 15 | start = i 16 | break 17 | lines = lines[start:] #删除无关内容 18 | 19 | for i in range(len(lines)): 20 | if lines[i].startswith('intervals ['): 21 | sum_indices.append(i) 22 | sum_indices.append(len(lines)) 23 | for i in range(len(sum_indices)-1): 24 | data = lines[sum_indices[i]:sum_indices[i+1]] 25 | # 提取 intervals 编号 26 | interval_key = re.search(r'\d+', data[0]).group() 27 | phone_count = {interval_key: {}} 28 | # 处理后续的键值对 29 | for item in data[1:]: 30 | key, value = item.split('=', 1) 31 | key = key.strip() 32 | value = value.strip() 33 | value = value.strip('"') 34 | phone_count[interval_key][key] = value 35 | phones_dict.update(phone_count) 36 | # print(phones_dict ) 37 | return long,phones_dict 38 | 39 | #需要传入TextGrid文件路径 40 | def run(path_main): 41 | json_data = {} 42 | for root,dirs,files in os.walk(path_main): 43 | for file in files: 44 | #如果文件是TextGrid文件 45 | if file.endswith(".TextGrid"): 46 | #读取文件内容 47 | with open(os.path.join(root, file), 'r', encoding='utf-8') as f: 48 | sum_data = {} 49 | data = [] 50 | textgrid_content = f.read() 51 | # 转换为JSON 52 | data = textgrid_change(textgrid_content) 53 | # json_data = dict(wav=file) 54 | sum_data[file.replace('.TextGrid','.wav')] = dict(wav_long=data[0],phones=data[1]) 55 | json_data.update(sum_data) 56 | # print(json_data) 57 | json_string = json.dumps(json_data, indent=4, ensure_ascii=False,separators=(',', ':')) 58 | json_dir = os.path.join(path_main, 'json') 59 | # 2. 创建目录(如果不存在) 60 | os.makedirs(json_dir, exist_ok=True) 61 | # 将JSON字符串写入文件 62 | with open(os.path.join(json_dir, 'ds_phone.json'), 'w', encoding='utf-8') as f: 63 | f.write(json_string) 64 | print(f"转换完成,结果已保存为{json_dir}\ds_phone.json") 65 | 66 | if __name__ == '__main__': 67 | # 读取TextGrid文件内容 68 | #设置工作目录 69 | path_main = "G:/编程/utau自动标注/F3" 70 | #遍历目录下的所有文件 71 | run(path_main+'/TextGrid') 72 | -------------------------------------------------------------------------------- /textgrid2json/del_SP.py: -------------------------------------------------------------------------------- 1 | import re 2 | from pathlib import Path 3 | 4 | def process_textgrid(file_path): 5 | with open(file_path, 'r', encoding='utf-8') as f: 6 | content = f.read() 7 | 8 | # 按 tiers 分割内容 9 | tiers = re.split(r'item \[\d+\]:', content)[1:] 10 | processed_tiers = [] 11 | 12 | for tier in tiers: 13 | # 提取所有 intervals 14 | intervals = re.findall(r'intervals \[\d+\]:\s+xmin = ([\d.]+)\s+xmax = ([\d.]+)\s+text = "([^"]+)"', tier) 15 | new_intervals = [] 16 | i = 0 17 | while i < len(intervals): 18 | if i > 0 and i < len(intervals) - 1 and intervals[i][2] == "SP": 19 | # 中间的 SP 区间,删除并调整前一个区间的 xmax 20 | if new_intervals: 21 | new_intervals[-1] = (new_intervals[-1][0], intervals[i + 1][0], new_intervals[-1][2]) 22 | i += 1 23 | else: 24 | new_intervals.append(intervals[i]) 25 | i += 1 26 | 27 | # 重新构建 tier 内容 28 | new_tier = re.sub(r'intervals: size = \d+', f'intervals: size = {len(new_intervals)}', tier) 29 | interval_texts = [] 30 | for j, (xmin, xmax, text) in enumerate(new_intervals, start=1): 31 | interval_text = f' intervals [{j}]:\n xmin = {xmin}\n xmax = {xmax}\n text = "{text}"' 32 | interval_texts.append(interval_text) 33 | interval_section = '\n'.join(interval_texts) 34 | new_tier = re.sub(r'(intervals: size = \d+\n)(.*?)(?=item \[\d+\]|$)', f'\\1{interval_section}\n', new_tier, flags=re.DOTALL) 35 | processed_tiers.append(new_tier) 36 | 37 | # 重新组合处理后的内容 38 | header = re.match(r'(.*?)item \[\d+\]:', content, re.DOTALL).group(1) 39 | processed_content = header + ''.join([f'item [{i + 1}]:{tier}' for i, tier in enumerate(processed_tiers)]) 40 | 41 | return processed_content 42 | def process_all_textgrid_files(input_dir): 43 | """ 44 | 遍历输入文件夹下所有 TextGrid 文件,处理后直接覆盖原文件内容。 45 | 46 | :param input_dir: 包含 TextGrid 文件的输入文件夹路径 47 | """ 48 | # 遍历输入文件夹下所有 TextGrid 文件 49 | for file_path in Path(input_dir).rglob('*.TextGrid'): 50 | try: 51 | # 处理文件 52 | processed_content = process_textgrid(file_path) 53 | 54 | # 直接覆盖原文件内容 55 | with open(file_path, 'w', encoding='utf-8') as f: 56 | f.write(processed_content) 57 | print(f"已处理并覆盖 {file_path}") 58 | except Exception as e: 59 | print(f"处理 {file_path} 时出错: {e}") 60 | 61 | if __name__ == "__main__": 62 | # 指定输入文件夹路径 63 | input_directory = r'F:\Download\utau数据集\日语粗标\TextGrid' 64 | process_all_textgrid_files(input_directory) 65 | 66 | 67 | # # 读取文件 68 | # file_path = r'F:\Download\utau数据集\日语粗标\TextGrid\0e24a3b8a39c5b6d6503a1d1cafe5a29441089b5b4947c56b2334b69ebdcd7fc.TextGrid' 69 | # processed_content = process_textgrid(file_path) 70 | # 71 | # # 保存处理后的内容 72 | # output_file_path = 'processed_textgrid.txt' 73 | # with open(output_file_path, 'w', encoding='utf-8') as f: 74 | # f.write(processed_content) -------------------------------------------------------------------------------- /textgrid2json/ds_json2filter.py: -------------------------------------------------------------------------------- 1 | #主要任务,删除AP,SP以及其他DS音素中不存在的音素 2 | #读取DS字典 3 | #把内容转换为拼音 4 | #分辨V和CV 5 | 6 | import json 7 | 8 | #音素获取 9 | def ds_dict_read(ds_dictpath,ignore): 10 | vowels = [] 11 | consonant =[] 12 | try: 13 | with open(ds_dictpath, 'r',encoding='utf-8') as f: 14 | for line in f: 15 | line = line.split() 16 | if len(line) == 3: 17 | consonant.append(line[1]) 18 | vowels.append(line[2]) 19 | elif len(line) == 4: 20 | consonant.append(line[2]) 21 | vowels.append(line[2]) 22 | vowels.append(line[3]) 23 | elif len(line) == 2: 24 | vowels.append(line[1]) 25 | except FileNotFoundError: 26 | print(f"错误:指定的文件 {ds_dictpath} 读取失败,请检查文件路径是否正确。") 27 | input('按任意键退出') 28 | exit() 29 | ignore = ignore.split(',') 30 | #忽略音素 31 | vowels = [vowel for vowel in vowels if vowel not in ignore] 32 | consonant = [con for con in consonant if con not in ignore] 33 | vowels = set(vowels) 34 | consonant = set(consonant) 35 | print(len(consonant),consonant) 36 | print(len(vowels),vowels) 37 | return vowels,consonant 38 | 39 | #删除不存在的音素 40 | #传入json数据和有效列表 41 | def filter_json_data(json_data, valid_list): 42 | for key, value in json_data.items(): 43 | phones = value.get('phones', {}) 44 | keys_to_remove = [] 45 | for phone_key, phone_value in phones.items(): 46 | text = phone_value.get('text') 47 | if text and text not in valid_list: 48 | keys_to_remove.append(phone_key) 49 | for key_to_remove in keys_to_remove: 50 | del phones[key_to_remove] 51 | json_data=reorganize_json_data(json_data) 52 | return json_data 53 | 54 | #添加-和R,首尾音素 55 | def reorganize_json_data(json_data): 56 | for audio_file, data in json_data.items(): 57 | phones = data.get('phones', {}) 58 | wav_long = data.get('wav_long', []) 59 | 60 | if not phones: 61 | continue 62 | 63 | # 获取排序后的phone keys 64 | sorted_keys = sorted(phones.keys(), key=lambda x: int(x)) 65 | 66 | # 处理第一个元素 67 | first_key = sorted_keys[0] 68 | first_phone = phones[first_key] 69 | # if float(first_phone['xmin']) != 0: 70 | new_phone = { 71 | "xmin": "0.0", 72 | "xmax": first_phone['xmin'], 73 | "text": "-" 74 | } 75 | # 创建新的有序字典 76 | new_phones = {"1": new_phone} 77 | for i, key in enumerate(sorted_keys, 2): 78 | new_phones[str(i)] = phones[key] 79 | data['phones'] = new_phones 80 | sorted_keys = sorted(new_phones.keys(), key=lambda x: int(x)) # 更新排序后的keys 81 | 82 | # 处理最后一个元素 83 | last_key = sorted_keys[-1] 84 | last_phone = data['phones'][last_key] 85 | wav_end = wav_long[1] if len(wav_long) >= 2 else 0 86 | if float(last_phone['xmax']) < wav_end: 87 | new_phone = { 88 | "xmin": last_phone['xmax'], 89 | "xmax": str(wav_end), 90 | "text": "R" 91 | } 92 | # 添加新的最后一个元素 93 | new_index = str(int(sorted_keys[-1]) + 1) 94 | data['phones'][new_index] = new_phone 95 | 96 | return json_data 97 | 98 | 99 | 100 | def run(ds_dict,json_path,ignore): 101 | with open(json_path, 'r', encoding='utf-8') as f: 102 | json_data = json.load(f) 103 | #音素生成 104 | valid_list = ds_dict_read(ds_dict,ignore) 105 | # print(valid_list) 106 | #过滤 107 | filtered_data = filter_json_data(json_data, valid_list[0].union(valid_list[1])) 108 | # 将过滤后的数据写回文件 109 | # print(filtered_data) 110 | newjson_path=json_path.split('.json')[0]+'_filter.json' 111 | # print(newjson_path) 112 | with open(newjson_path, 'w', encoding='utf-8') as f: 113 | json.dump(filtered_data, f, ensure_ascii=False, indent=4) 114 | print('写入成功') 115 | 116 | if __name__ == '__main__': 117 | json_path = r'E:\OpenUtau\Singers\XIABAI_new_CHN_CVVC_F3_autooto\F3/TextGrid\json\ds_phone.json' 118 | ds_dict = 'SOFA-UTAUCHN-dic.txt' 119 | ignore = 'AP,SP' 120 | run(ds_dict,json_path,ignore) 121 | -------------------------------------------------------------------------------- /textgrid2json/ds_json2word.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | # def ds_dict_read(ds_dictpath): 5 | # ds_dict = {} 6 | # vowels = [] 7 | # consonant =[] 8 | # CV_pinyin = [] 9 | # V_pinyin = [] 10 | # with open(ds_dictpath, 'r') as f: 11 | # for line in f: 12 | # line = line.split() 13 | # if len(line) == 3: 14 | # CV_pinyin.append(line[0]) 15 | # consonant.append(line[1]) 16 | # vowels.append(line[2]) 17 | # elif len(line) == 2: 18 | # V_pinyin.append(line[0]) 19 | # vowels.append(line[1]) 20 | # CV_pinyin = set(CV_pinyin) 21 | # V_pinyin = set(V_pinyin) 22 | # vowels = set(vowels) 23 | # consonant = set(consonant) 24 | # # print(len(CV_pinyin),CV_pinyin) 25 | # # print(len(V_pinyin),V_pinyin) 26 | # # print(len(consonant),consonant) 27 | # # print(len(vowels),vowels) 28 | # return vowels,consonant,CV_pinyin,V_pinyin 29 | 30 | 31 | # 新增拼音映射字典 32 | def build_pinyin_map(ds_dictpath): 33 | phone_map = {} # 音素序列 -> 拼音 34 | max_length = 0 35 | with open(ds_dictpath, 'r',encoding='utf-8') as f: 36 | for line in f: 37 | parts = line.strip().split() 38 | if len(parts) >= 2: 39 | key = tuple(parts[1:]) # 音素组合作为元组 40 | phone_map[key] = parts[0] 41 | max_length = max(max_length, len(key)) 42 | # print("当前字典内容:", phone_map) 43 | print("最大音素长度:", max_length) 44 | return phone_map, max_length 45 | 46 | 47 | def phones2word(json_data,ds_dictpath): 48 | phone_map, max_length = build_pinyin_map(ds_dictpath) 49 | 50 | for audio_file, data in json_data.items(): 51 | phones = data.get('phones', {}) 52 | if not phones: 53 | continue 54 | 55 | sorted_phones = sorted(phones.items(), key=lambda x: int(x[0])) 56 | new_phones = {} 57 | phone_counter = 1 58 | i = 0 59 | 60 | while i < len(sorted_phones): 61 | current_phone = sorted_phones[i][1] 62 | current_text = current_phone['text'] 63 | 64 | # 处理特殊符号(新增冒号类符号判断) 65 | if current_text in ('-', 'R'): 66 | new_phones[str(phone_counter)] = { 67 | "xmin": current_phone['xmin'], 68 | "middle": current_phone['xmin'], 69 | "xmax": current_phone['xmax'], 70 | "text": current_text 71 | } 72 | phone_counter += 1 73 | i += 1 74 | continue 75 | 76 | matched = False 77 | candidates = [] 78 | max_possible = min(max_length, len(sorted_phones) - i) 79 | for k in range(max_possible, 0, -1): 80 | # 修改2:允许包含冒号的音素参与组合 81 | candidate = tuple([sorted_phones[i + m][1]['text'] for m in range(k)]) 82 | # print(f"尝试匹配: i={i}, k={k}, 序列={candidate}") 83 | candidates.append((k, candidate)) # 移除过滤条件 84 | 85 | # 优化2:按候选长度降序处理 86 | for k, current_sequence in sorted(candidates, key=lambda x: -x[0]): 87 | # print(f"正式匹配尝试: i={i}, k={k}, 序列={current_sequence}") 88 | if current_sequence in phone_map: 89 | # print(f"匹配成功: i={i}, k={k}, 序列={current_sequence}") 90 | # 修复缩进问题 91 | start_phone = sorted_phones[i][1] 92 | end_phone = sorted_phones[i + k - 1][1] 93 | if start_phone['xmax'] == end_phone['xmax']: 94 | new_phones[str(phone_counter)] = { 95 | "xmin": start_phone['xmin'], 96 | "middle": start_phone['xmin'], 97 | "xmax": end_phone['xmax'], 98 | "text": phone_map[current_sequence] 99 | } 100 | phone_counter += 1 101 | i += k 102 | matched = True 103 | break 104 | new_phones[str(phone_counter)] = { 105 | "xmin": start_phone['xmin'], 106 | "middle": start_phone['xmax'], 107 | "xmax": end_phone['xmax'], 108 | "text": phone_map[current_sequence] 109 | } 110 | phone_counter += 1 111 | i += k 112 | matched = True 113 | break 114 | # 合并k个音素 115 | start_phone = sorted_phones[i][1] 116 | end_phone = sorted_phones[i + k - 1][1] 117 | new_phones[str(phone_counter)] = { 118 | "xmin": start_phone['xmin'], 119 | "middle": start_phone['xmax'], 120 | "xmax": end_phone['xmax'], 121 | "text": phone_map[current_sequence] 122 | } 123 | phone_counter += 1 124 | i += k 125 | matched = True 126 | break 127 | # ... existing special character handling ... 128 | if not matched: 129 | # 无法合并则单独处理当前音素 130 | print(f"无法合并的音素:{current_text}") 131 | current_phone = sorted_phones[i][1] 132 | new_phones[str(phone_counter)] = { 133 | "xmin": current_phone['xmin'], 134 | "middle": current_phone['xmin'], 135 | "xmax": current_phone['xmax'], 136 | "text": current_phone['text'] 137 | } 138 | phone_counter += 1 139 | i += 1 140 | # 用新结构替换原phones数据 141 | data['phones'] = new_phones 142 | # print(new_phones) 143 | 144 | def run(ds_dictpath,json_path): 145 | # valid_list = ds_dict_read(ds_dictpath) 146 | with open(json_path, 'r', encoding='utf-8') as f: 147 | json_data = json.load(f) 148 | phones2word(json_data,ds_dictpath) 149 | new_path = os.path.dirname(json_path)+'/word_phone.json' 150 | with open(new_path, 'w', encoding='utf-8') as f: 151 | json.dump(json_data, f, ensure_ascii=False, indent=4) 152 | print(f'合并后的文件已保存至: {new_path}') 153 | 154 | 155 | if __name__ == "__main__": 156 | ds_dictpath = 'F:/aising\SOFA_AI-main\sofa_ckpt\Mandarin_three-stage\Mandarin_three-stage_dictionary_New.txt' 157 | json_path = 'E:\OpenUtau\Singers\XIABAI_new_CHN_CVVC_F3_autooto\F3/TextGrid/json/ds_phone_filter.json' 158 | run(ds_dictpath,json_path) -------------------------------------------------------------------------------- /textgrid2json/transcriptions_make.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import json 3 | import os 4 | import re 5 | 6 | def ds_dict_read(ds_dictpath): 7 | ds_dict = {} 8 | with open(ds_dictpath, 'r',encoding='utf-8') as f: 9 | word_list = f.read().strip().split('\n') 10 | for word in word_list: 11 | word = word.split('\t') 12 | ds_dict[word[0]] = word[1] 13 | return ds_dict 14 | 15 | def create_transcriptions_csv(folder_path,ds_dictpath): 16 | csv_file_path = os.path.join(folder_path, 'transcriptions.csv') 17 | existing_data = [] 18 | existing_names = set() 19 | # 如果 CSV 文件存在,读取其内容 20 | if os.path.exists(csv_file_path): 21 | with open(csv_file_path, 'r', newline='', encoding='utf-8') as csvfile: 22 | reader = csv.reader(csvfile) 23 | headers = next(reader) # 读取表头 24 | existing_data.append(headers) 25 | for row in reader: 26 | name = row[0] 27 | # lab = row[3] 28 | # 如果 lab 列为空,尝试查找对应的 .lab 文件 29 | # if not lab: 30 | # lab_file_path = os.path.join(folder_path, f"{name}.lab") 31 | # if os.path.exists(lab_file_path): 32 | # with open(lab_file_path, 'r', encoding='utf-8') as lab_file: 33 | # row[3] = lab_file.read().strip() 34 | # existing_data.append(row) 35 | existing_names.add(name) 36 | else: 37 | # 如果 CSV 文件不存在,创建一个空的 CSV 文件 38 | with open(csv_file_path, 'w', newline='', encoding='utf-8') as csvfile: 39 | writer = csv.writer(csvfile) 40 | writer.writerow(['name', 'ph_seq', 'ph_dur', 'words']) 41 | 42 | new_data = [] if existing_data else [['name', 'ph_seq', 'ph_dur', 'words']] 43 | 44 | # 遍历文件夹中的所有文件 45 | for root, dirs, files in os.walk(folder_path): 46 | for file in files: 47 | if file.endswith('.wav'): 48 | # 去除 .wav 后缀 49 | name = os.path.splitext(file)[0] 50 | if name not in existing_names: 51 | # 初始化 ph_seq 和 ph_dur 列(暂时为空) 52 | ph_seq = "" 53 | ph_dur = "" 54 | # 查找对应的 .lab 文件 55 | lab_file_path = os.path.join(root, f"{name}.lab") 56 | if os.path.exists(lab_file_path): 57 | # 读取 .lab 文件内容 58 | with open(lab_file_path, 'r', encoding='utf-8') as lab_file: 59 | lab = lab_file.read().strip() 60 | else: 61 | lab = "" 62 | # 添加一行数据到列表 63 | new_data.append([name, ph_seq, ph_dur, lab]) 64 | ds_dict = ds_dict_read(ds_dictpath) 65 | 66 | 67 | 68 | # 合并现有数据和新数据 69 | all_data = existing_data + new_data if existing_data else new_data 70 | for i in range(len(all_data)): 71 | if all_data[i][3] == '': 72 | lab_file_path = os.path.join(folder_path, f"{all_data[i][0]}.lab") 73 | if os.path.exists(lab_file_path): 74 | with open(lab_file_path, 'r', encoding='utf-8') as lab_file: 75 | all_data[i][3] = lab_file.read().strip() 76 | else: 77 | all_data[i][3] = '' 78 | for i in range(len(all_data)): 79 | phones = [] 80 | if all_data[i][1] == '' and all_data[i][3] != '': 81 | phones.append('SP') 82 | for word in all_data[i][3].split(' '): 83 | if word in ds_dict: 84 | phones.append(ds_dict[word]) 85 | # phones.append('R') 86 | else: 87 | phones.append(word) 88 | print(f"{all_data[i][0]}:{word}不存在于sofa字典中") 89 | if phones[-1]!='R' and phones[-1]!='B': 90 | phones.append('R') 91 | phones.append('SP') 92 | all_data[i][1] = ' '.join(phones) 93 | # print(all_data[i][1]) 94 | # 写入或更新 transcriptions.csv 文件 95 | with open(csv_file_path, 'w', newline='', encoding='utf-8') as csvfile: 96 | writer = csv.writer(csvfile) 97 | writer.writerows(all_data) 98 | 99 | if __name__ == '__main__': 100 | # 示例调用,需替换为实际文件夹路径 101 | folder_path = r'F:\Download\utau数据集\有授权\中文\中文拼接数据_自有授权\baini_Chn_CVXY\E3' 102 | create_transcriptions_csv(folder_path,r'F:\Download\utau数据集\合并.txt') -------------------------------------------------------------------------------- /textgrid2json/word2utau_phone.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import os 4 | 5 | #读取presamp.ini文件 6 | def load_presamp(ini_path): 7 | word2phone = {} 8 | word_phone = {} 9 | try: 10 | with open(ini_path, 'r',encoding='utf-8') as file: 11 | ini_text = file.read() 12 | # 提取 [VOWEL] 部分 13 | vowel_match = re.search(r'\[VOWEL\](.*?)\[', ini_text, re.DOTALL) 14 | vowel_section = vowel_match.group(1).strip() 15 | # 提取 [CONSONANT] 部分 16 | consonant_match = re.search(r'\[CONSONANT\](.*?)\[', ini_text, re.DOTALL) 17 | if consonant_match != None: 18 | consonant_section = consonant_match.group(1).strip() 19 | else: 20 | print('未找到[CONSONANT]部分,仅使用[VOWEL]部分生成') 21 | for vowel in vowel_section.split('\n'): 22 | vowel = vowel.split('=') 23 | phone = vowel[0] 24 | for v in vowel[2].split(','): 25 | word2phone.update({v:{'V':phone}}) 26 | if consonant_match != None: 27 | for consonant in consonant_section.split('\n'): 28 | consonant2 = consonant.split('=') 29 | phone = consonant2[0] 30 | for c in consonant2[1].split(','): 31 | word2phone[c].update({'C': phone}) 32 | else: 33 | for vowel in vowel_section.split('\n'): 34 | vowel = vowel.split('=') 35 | for c in vowel[2].split(','): 36 | word2phone[c].update({'C': c}) 37 | for key, value in word2phone.items(): 38 | if 'C' in value: 39 | word_phone[key]=[value['C'],value['V']] 40 | else: 41 | word_phone[key]=[value['V']] 42 | word_phone2 = dict(sorted(word_phone.items(), key=lambda item: len(item[0]), reverse=True)) 43 | # print(word_phone2) 44 | return word_phone2 45 | except Exception as e: 46 | import traceback 47 | traceback.print_exc() 48 | input(f"读取presamp.ini失败: \n请检查是否存在[VOWEL]和[CONSONANT]部分") 49 | 50 | 51 | 52 | def split_pinyin_to_phones(word_data, mappings): 53 | """拆分单个拼音项为音素""" 54 | new_phones = {} 55 | phone_counter = 1 56 | 57 | for idx, (key, item) in enumerate(sorted(word_data['phones'].items(), 58 | key=lambda x: int(x[0]))): 59 | pinyin = item['text'] 60 | # print(item) 61 | # 保留特殊符号 62 | if pinyin in ('-', 'R'): 63 | new_phones[str(phone_counter)] = item 64 | phone_counter += 1 65 | continue 66 | 67 | # 获取音素分解 68 | phones = mappings.get(pinyin, [pinyin]) 69 | 70 | # 时间分配逻辑 71 | if len(phones) == 2: # CV结构 72 | # print(phones) 73 | # print(item) 74 | consonant = { 75 | 76 | "xmin": item['xmin'], 77 | "xmax": item['middle'], 78 | "text": phones[0] 79 | } 80 | vowel = { 81 | "xmin": item['middle'], 82 | "xmax": item['xmax'], 83 | "text": phones[1] 84 | } 85 | new_phones[str(phone_counter)] = consonant 86 | new_phones[str(phone_counter + 1)] = vowel 87 | phone_counter += 2 88 | else: # 单个音素 89 | # print(phones) 90 | new_phones[str(phone_counter)] = { 91 | "xmin": item['xmin'], 92 | "xmax": item['xmax'], 93 | "text": phones[0] 94 | } 95 | phone_counter += 1 96 | return new_phones 97 | 98 | 99 | def generate_utau_phone(presamp_path, word_json_path): 100 | 101 | word_phone = load_presamp(presamp_path) 102 | 103 | with open(word_json_path, 'r', encoding='utf-8') as f: 104 | word_data = json.load(f) 105 | 106 | for audio_file, data in word_data.items(): 107 | data['phones'] = split_pinyin_to_phones(data, word_phone) 108 | new_path = os.path.dirname(word_json_path)+'/utau_phone.json' 109 | with open(new_path, 'w', encoding='utf-8') as f: 110 | json.dump(word_data, f, ensure_ascii=False, indent=4) 111 | 112 | 113 | if __name__ == "__main__": 114 | generate_utau_phone( 115 | presamp_path='../presamp/CVR中文-presamp.ini', 116 | word_json_path='E:\OpenUtau\Singers/baini_Chn_CVXY\E3/TextGrid/json/word_phone.json', 117 | ) -------------------------------------------------------------------------------- /wavname2lab.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | # 定义函数来处理文件名 4 | def process_wav_name(wav_name,cuts): 5 | # 移除文件扩展名 6 | cleaned_name = os.path.splitext(wav_name)[0] 7 | # 剔除下划线 8 | hira_list=sorted(['いぇ','うぇ','うぃ','うぉ','きゃ','きゅ','きぇ','きょ','ぎゃ','ぎゅ','ぎぇ','ぎょ','くぁ','くぃ','くぇ','くぉ','ぐぁ','ぐぃ','ぐぇ','ぐぉ','しゃ','しゅ','しぇ','しょ','じゃ','じぇ','じゅ','じょ','すぃ','ずぃ','ちゃ','ちゅ','ちぇ','ちょ','つぁ','つぃ','つぇ','つぉ','てぃ','てゅ','でぃ','でゅ','とぅ','どぅ','にゃ','にぇ','にゅ','にょ','ひゃ','ひゅ','ひぇ','ひょ','びゃ','びゅ','びぇ','びょ','ぴゃ','ぴゅ','ぴぇ','ぴょ','ふぁ','ふぃ','ふぇ','ふぉ','ぶぁ','ぶぃ','ぶぇ','ぶぉ','みゃ','みゅ','みぇ','みょ','りゃ','りゅ','りぇ','りょ','ヴぁ','ヴぃ','ヴぇ','ヴぉ','ヴ','ガ','ギ','グ','ゲ','ゴ','あ','い','う','え','お','か','が','き','ぎ','く','ぐ','け','げ','こ','ご','さ','ざ','し','じ','す','ず','せ','ぜ','そ','ぞ','た','だ','ち','つ','て','で','と','ど','な','に','ぬ','ね','の','は','ば','ぱ','ひ','び','ぴ','ふ','ぶ','ぷ','へ','べ','ぺ','ほ','ぼ','ぽ','ま','み','む','め','も','や','ゆ','よ','ら','り','る','れ','ろ','わ','ん'] 9 | , key=lambda x: len(x), reverse=True) 10 | 11 | for cut in cuts: 12 | cleaned_name = cleaned_name.replace(cut,' ') 13 | # 构建正则表达式模式(转义特殊字符) 14 | 15 | pattern = re.compile('|'.join(map(re.escape, hira_list))) # 使用正则表达式优化匹配[4,7](@ref) 16 | 17 | # 执行替换:匹配项之间插入空格 18 | cleaned_name = pattern.sub(lambda m: f'{m.group()} ', cleaned_name) 19 | 20 | cleaned_name=cleaned_name.replace(' ',' ') 21 | 22 | if cleaned_name[0]==' ': 23 | cleaned_name=cleaned_name[1:] 24 | return cleaned_name 25 | 26 | #传入wav路径(可选加入自定义分隔符号) 27 | def run(path,cuts): 28 | wav_files = [f for f in os.listdir(path) if f.endswith('.wav')] 29 | # 处理每个WAV文件 30 | for wav_file in wav_files: 31 | # 处理文件名 32 | lab_content = process_wav_name(wav_file,cuts) 33 | # # 生成.lab文件的内容 34 | # lab_content = ' '.join(pinyin_parts).strip() 35 | # 生成.lab文件的文件名 36 | lab_file_name = os.path.splitext(wav_file)[0] + '.lab' 37 | # 写入.lab文件 38 | with open(path + '/' + lab_file_name, 'w', encoding='utf-8') as lab_file: 39 | lab_file.write(lab_content) 40 | print(lab_content) 41 | print(f"已生成 {lab_file_name}") 42 | 43 | 44 | if __name__ == '__main__': 45 | path = r'F:\Download\utau数据集\有授权\中文\中文拼接数据_自有授权\xiabai CVVC JIANHUA TEST\D4' 46 | # 获取当前目录下所有的WAV文件 47 | run(path,['_','-']) 48 | 49 | 50 | -------------------------------------------------------------------------------- /音频拼接并生成lab.py: -------------------------------------------------------------------------------- 1 | import wave 2 | import os 3 | 4 | import wave 5 | import os 6 | 7 | 8 | def concatenate_audio_files(folder_path): 9 | # 创建新文件夹存放结果 10 | output_folder = os.path.join(folder_path, 'combined') 11 | os.makedirs(output_folder, exist_ok=True) 12 | 13 | # 获取所有wav文件并按文件名排序 14 | wav_files = sorted([f for f in os.listdir(folder_path) if f.endswith('.wav')], 15 | key=lambda x: x.split('.')[0]) 16 | 17 | lab_content = [] 18 | 19 | # 以三个为一组处理文件 20 | for i in range(0, len(wav_files), 3): 21 | group = wav_files[i:i + 3] 22 | # 生成组合文件名(不带扩展名) 23 | base_names = [os.path.splitext(f)[0] for f in group] 24 | combined_name = '_'.join(base_names) + '.wav' 25 | lab_content.append(combined_name[:-4]) # 记录用于lab文件的内容 26 | 27 | # 拼接音频 28 | output_path = os.path.join(output_folder, combined_name) 29 | with wave.open(output_path, 'wb') as output_wave: 30 | for filename in group: 31 | filepath = os.path.join(folder_path, filename) 32 | with wave.open(filepath, 'rb') as input_wave: 33 | if output_wave.getnframes() == 0: 34 | output_wave.setparams(input_wave.getparams()) 35 | output_wave.writeframes(input_wave.readframes(input_wave.getnframes())) 36 | 37 | # 保存lab文件到原文件夹 38 | # output_lab_path = os.path.join(folder_path, 'output.lab') 39 | # with open(output_lab_path, 'w', encoding='utf-8') as lab_file: 40 | # lab_file.write(" ".join(lab_content)) 41 | 42 | print(f"生成{len(lab_content)}个音频文件到 {output_folder}") 43 | # print(f".lab文件生成完成,保存为 {output_lab_path}") 44 | 45 | 46 | # ... 剩余代码保持不变 ... 47 | 48 | # 调用函数,将文件夹路径替换为实际的文件夹路径 49 | folder_path = 'E:\OpenUtau\Singers\Weiyin3.0\CV' 50 | concatenate_audio_files(folder_path) --------------------------------------------------------------------------------