├── .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)
--------------------------------------------------------------------------------