├── .gitignore
├── License
├── README.md
├── __jamo.py
├── audio_loader_for_gui.py
├── audio_player_for_gui.py
├── data
└── KoreanSyllableData
│ ├── U+11xx.json
│ └── U+31xx.json
├── pyanimalese_cli.py
├── pyanimalese_gui.py
├── requirements.txt
└── sources
├── 01.padata
├── 02.padata
├── 03.padata
├── 04.padata
├── 05.padata
├── 06.padata
├── 07.padata
├── 08.padata
├── 09.padata
├── 10.padata
├── 11.padata
├── 12.padata
├── 13.padata
├── 14.padata
├── 15.padata
├── 16.padata
├── 17.padata
├── 18.padata
├── 19.padata
├── 20.padata
└── high
├── 01.padata
├── 02.padata
├── 03.padata
├── 04.padata
├── 05.padata
├── 06.padata
├── 07.padata
├── 08.padata
├── 09.padata
├── 10.padata
├── 11.padata
├── 12.padata
├── 13.padata
├── 14.padata
├── 15.padata
├── 16.padata
├── 17.padata
├── 18.padata
├── 19.padata
└── 20.padata
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
131 | .idea/
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Ju Hwijung
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PyAnimalese에 대하여
2 | 동물의 숲 NPC 목소리로 문장을 읽어주는 프로그램입니다.
3 |
4 | ## 지원 언어
5 | - 한국어
6 |
7 | ## 시연영상
8 | - [링크(YouTube)](https://www.youtube.com/watch?v=MsUTd79QnuI)
9 |
10 | ## 원리
11 | 1. 한글로 문자열을 입력 받는다.
12 | 2. 문자열을 순회하면서 자모를 분리해서 초성만 얻어낸다.
13 | 3. 미리 녹음해둔 해당 초성의 발음파일(e.g. 'ㄱ'은 '그' 처럼 어중간하게 발음함)을 가져온다.
14 | 4. 피치를 높인다. 이 때, 랜덤으로 피치 값을 조절한다.
15 | 5. 하나로 합쳐서 재생(저장)한다.
16 |
17 | ## 실행하기
18 | - 방법1: pyanimalese_cli.py를 직접 실행합니다. 이때, sources 폴더가 필요하며 아래의 디펜던시 패키지를 설치해야합니다.
19 | - 방법2: 릴리즈 버전을 다운로드하여 GUI로 사용합니다. [바로가기](https://github.com/hwi-middle/PyAnimalese/releases)
20 |
21 | 주의: 두 방법 모두 ffmpeg이 설치되어있어야합니다.
22 |
23 | ## 디펜던시
24 | - [Pydub](https://github.com/jiaaro/pydub)
25 | - [Jamo](https://github.com/JDongian/python-jamo)
26 | - [PyAudio](https://people.csail.mit.edu/hubert/pyaudio/)
27 |
28 | ## 디펜던시 패키지 설치 (Windows 기준)
29 | ```pip install -r requirements.txt```
30 |
31 | ```pipwin install pyaudio```
32 |
33 | 주의: PyAudio는 pipwin으로 별도로 설치해야합니다.
34 |
35 |
--------------------------------------------------------------------------------
/__jamo.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Syllable and jamo analysis for Korean. Default internal exchange form is
3 | Hangul characters, not codepoints. Jamo exchange form is U+11xx characters,
4 | not U+3xxx Hangul Compatibility Jamo (HCJ) characters or codepoints.
5 |
6 | For more information, see:
7 | http://python-jamo.readthedocs.org/ko/latest/
8 | """
9 |
10 | import sys
11 | import os
12 | from sys import stderr
13 | from itertools import chain
14 | import json
15 | import re
16 |
17 |
18 | _ROOT = '\\'.join(sys.executable.split('\\')[0:-1]) + '\\data'
19 |
20 | _JAMO_OFFSET = 44032
21 | _JAMO_LEAD_OFFSET = 0x10ff
22 | _JAMO_VOWEL_OFFSET = 0x1160
23 | _JAMO_TAIL_OFFSET = 0x11a7
24 |
25 | with open(os.path.join(_ROOT, 'KoreanSyllableData', "U+11xx.json"), 'r') as namedata:
26 | _JAMO_TO_NAME = json.load(namedata)
27 | _JAMO_REVERSE_LOOKUP = {name: char for char, name in _JAMO_TO_NAME.items()}
28 | with open(os.path.join(_ROOT, 'KoreanSyllableData', "U+31xx.json"), 'r') as namedata:
29 | _HCJ_TO_NAME = json.load(namedata)
30 | _HCJ_REVERSE_LOOKUP = {name: char for char, name in _HCJ_TO_NAME.items()}
31 |
32 | JAMO_LEADS = [chr(_) for _ in range(0x1100, 0x115F)]
33 | JAMO_LEADS_MODERN = [chr(_) for _ in range(0x1100, 0x1113)]
34 | JAMO_VOWELS = [chr(_) for _ in range(0x1161, 0x11A8)]
35 | JAMO_VOWELS_MODERN = [chr(_) for _ in range(0x1161, 0x1176)]
36 | JAMO_TAILS = [chr(_) for _ in range(0x11A8, 0x1200)]
37 | JAMO_TAILS_MODERN = [chr(_) for _ in range(0x11A8, 0x11C3)]
38 |
39 |
40 | class InvalidJamoError(Exception):
41 | """jamo is a U+11xx codepoint."""
42 | def __init__(self, message, jamo):
43 | super(InvalidJamoError, self).__init__(message)
44 | self.jamo = hex(ord(jamo))
45 | print("Could not parse jamo: U+{code}".format(code=self.jamo[2:]),
46 | file=stderr)
47 |
48 |
49 | def _hangul_char_to_jamo(syllable):
50 | """Return a 3-tuple of lead, vowel, and tail jamo characters.
51 | Note: Non-Hangul characters are echoed back.
52 | """
53 | if is_hangul_char(syllable):
54 | rem = ord(syllable) - _JAMO_OFFSET
55 | tail = rem % 28
56 | vowel = 1 + ((rem - tail) % 588) // 28
57 | lead = 1 + rem // 588
58 | if tail:
59 | return (chr(lead + _JAMO_LEAD_OFFSET),
60 | chr(vowel + _JAMO_VOWEL_OFFSET),
61 | chr(tail + _JAMO_TAIL_OFFSET))
62 | else:
63 | return (chr(lead + _JAMO_LEAD_OFFSET),
64 | chr(vowel + _JAMO_VOWEL_OFFSET))
65 | else:
66 | return syllable
67 |
68 |
69 | def _jamo_to_hangul_char(lead, vowel, tail=0):
70 | """Return the Hangul character for the given jamo characters.
71 | """
72 | lead = ord(lead) - _JAMO_LEAD_OFFSET
73 | vowel = ord(vowel) - _JAMO_VOWEL_OFFSET
74 | tail = ord(tail) - _JAMO_TAIL_OFFSET if tail else 0
75 | return chr(tail + (vowel - 1) * 28 + (lead - 1) * 588 + _JAMO_OFFSET)
76 |
77 |
78 | def _jamo_char_to_hcj(char):
79 | if is_jamo(char):
80 | hcj_name = re.sub("(?<=HANGUL )(\w+)",
81 | "LETTER",
82 | _get_unicode_name(char))
83 | if hcj_name in _HCJ_REVERSE_LOOKUP.keys():
84 | return _HCJ_REVERSE_LOOKUP[hcj_name]
85 | return char
86 |
87 |
88 | def _get_unicode_name(char):
89 | """Fetch the unicode name for jamo characters.
90 | """
91 | if char not in _JAMO_TO_NAME.keys() and char not in _HCJ_TO_NAME.keys():
92 | raise InvalidJamoError("Not jamo or nameless jamo character", char)
93 | else:
94 | if is_hcj(char):
95 | return _HCJ_TO_NAME[char]
96 | return _JAMO_TO_NAME[char]
97 |
98 |
99 | def is_jamo(character):
100 | """Test if a single character is a jamo character.
101 | Valid jamo includes all modern and archaic jamo, as well as all HCJ.
102 | Non-assigned code points are invalid.
103 | """
104 | code = ord(character)
105 | return 0x1100 <= code <= 0x11FF or\
106 | 0xA960 <= code <= 0xA97C or\
107 | 0xD7B0 <= code <= 0xD7C6 or 0xD7CB <= code <= 0xD7FB or\
108 | is_hcj(character)
109 |
110 |
111 | def is_jamo_modern(character):
112 | """Test if a single character is a modern jamo character.
113 | Modern jamo includes all U+11xx jamo in addition to HCJ in modern usage,
114 | as defined in Unicode 7.0.
115 | WARNING: U+1160 is NOT considered a modern jamo character, but it is listed
116 | under 'Medial Vowels' in the Unicode 7.0 spec.
117 | """
118 | code = ord(character)
119 | return 0x1100 <= code <= 0x1112 or\
120 | 0x1161 <= code <= 0x1175 or\
121 | 0x11A8 <= code <= 0x11C2 or\
122 | is_hcj_modern(character)
123 |
124 |
125 | def is_hcj(character):
126 | """Test if a single character is a HCJ character.
127 | HCJ is defined as the U+313x to U+318x block, sans two non-assigned code
128 | points.
129 | """
130 | return 0x3131 <= ord(character) <= 0x318E and ord(character) != 0x3164
131 |
132 |
133 | def is_hcj_modern(character):
134 | """Test if a single character is a modern HCJ character.
135 | Modern HCJ is defined as HCJ that corresponds to a U+11xx jamo character
136 | in modern usage.
137 | """
138 | code = ord(character)
139 | return 0x3131 <= code <= 0x314E or\
140 | 0x314F <= code <= 0x3163
141 |
142 |
143 | def is_hangul_char(character):
144 | """Test if a single character is in the U+AC00 to U+D7A3 code block,
145 | excluding unassigned codes.
146 | """
147 | return 0xAC00 <= ord(character) <= 0xD7A3
148 |
149 |
150 | def get_jamo_class(jamo):
151 | """Determine if a jamo character is a lead, vowel, or tail.
152 | Integers and U+11xx characters are valid arguments. HCJ consonants are not
153 | valid here.
154 |
155 | get_jamo_class should return the class ["lead" | "vowel" | "tail"] of a
156 | given character or integer.
157 |
158 | Note: jamo class directly corresponds to the Unicode 7.0 specification,
159 | thus includes filler characters as having a class.
160 | """
161 | # TODO: Perhaps raise a separate error for U+3xxx jamo.
162 | if jamo in JAMO_LEADS or jamo == chr(0x115F):
163 | return "lead"
164 | if jamo in JAMO_VOWELS or jamo == chr(0x1160) or\
165 | 0x314F <= ord(jamo) <= 0x3163:
166 | return "vowel"
167 | if jamo in JAMO_TAILS:
168 | return "tail"
169 | else:
170 | raise InvalidJamoError("Invalid or classless jamo argument.", jamo)
171 |
172 |
173 | def jamo_to_hcj(data):
174 | """Convert jamo to HCJ.
175 | Arguments may be iterables or single characters.
176 |
177 | jamo_to_hcj should convert every jamo character into HCJ in a given input,
178 | if possible. Anything else is unchanged.
179 |
180 | jamo_to_hcj is the generator version of j2hcj, the string version. Passing
181 | a character to jamo_to_hcj will still return a generator.
182 | """
183 | return (_jamo_char_to_hcj(_) for _ in data)
184 |
185 |
186 | def j2hcj(jamo):
187 | """Convert jamo into HCJ.
188 | Arguments may be iterables or single characters.
189 |
190 | j2hcj should convert every jamo character into HCJ in a given input, if
191 | possible. Anything else is unchanged.
192 |
193 | j2hcj is the string version of jamo_to_hcj, the generator version.
194 | """
195 | return ''.join(jamo_to_hcj(jamo))
196 |
197 |
198 | def hcj_to_jamo(hcj_char, position="vowel"):
199 | """Convert a HCJ character to a jamo character.
200 | Arguments may be single characters along with the desired jamo class
201 | (lead, vowel, tail). Non-mappable input will raise an InvalidJamoError.
202 | """
203 | if position == "lead":
204 | jamo_class = "CHOSEONG"
205 | elif position == "vowel":
206 | jamo_class = "JUNGSEONG"
207 | elif position == "tail":
208 | jamo_class = "JONGSEONG"
209 | else:
210 | raise InvalidJamoError("No mapping from input to jamo.", hcj_char)
211 | jamo_name = re.sub("(?<=HANGUL )(\w+)",
212 | jamo_class,
213 | _get_unicode_name(hcj_char))
214 | # TODO: add tests that test non entries.
215 | if jamo_name in _JAMO_REVERSE_LOOKUP.keys():
216 | return _JAMO_REVERSE_LOOKUP[jamo_name]
217 | return hcj_char
218 |
219 |
220 | def hcj2j(hcj_char, position="vowel"):
221 | """Convert a HCJ character to a jamo character.
222 | Identical to hcj_to_jamo.
223 | """
224 | return hcj_to_jamo(hcj_char, position)
225 |
226 |
227 | def hangul_to_jamo(hangul_string):
228 | """Convert a string of Hangul to jamo.
229 | Arguments may be iterables of characters.
230 |
231 | hangul_to_jamo should split every Hangul character into U+11xx jamo
232 | characters for any given string. Non-hangul characters are not changed.
233 |
234 | hangul_to_jamo is the generator version of h2j, the string version.
235 | """
236 |
237 | return (_ for _ in
238 | chain.from_iterable(_hangul_char_to_jamo(_) for _ in
239 | hangul_string))
240 |
241 |
242 | def h2j(hangul_string):
243 | """Convert a string of Hangul to jamo.
244 | Arguments may be iterables of characters.
245 |
246 | h2j should split every Hangul character into U+11xx jamo for any given
247 | string. Non-hangul characters are not touched.
248 |
249 | h2j is the string version of hangul_to_jamo, the generator version.
250 | """
251 |
252 | return ''.join(hangul_to_jamo(hangul_string))
253 |
254 |
255 | def jamo_to_hangul(lead, vowel, tail=''):
256 | """Return the Hangul character for the given jamo input.
257 | Integers corresponding to U+11xx jamo codepoints, U+11xx jamo characters,
258 | or HCJ are valid inputs.
259 |
260 | Outputs a one-character Hangul string.
261 |
262 | This function is identical to j2h.
263 | """
264 | # Internally, we convert everything to a jamo char,
265 | # then pass it to _jamo_to_hangul_char
266 | lead = hcj_to_jamo(lead, "lead")
267 | vowel = hcj_to_jamo(vowel, "vowel")
268 | if not tail or ord(tail) == 0:
269 | tail = None
270 | elif is_hcj(tail):
271 | tail = hcj_to_jamo(tail, "tail")
272 | if (is_jamo(lead) and get_jamo_class(lead) == "lead") and\
273 | (is_jamo(vowel) and get_jamo_class(vowel) == "vowel") and\
274 | ((not tail) or (is_jamo(tail) and get_jamo_class(tail) == "tail")):
275 | result = _jamo_to_hangul_char(lead, vowel, tail)
276 | if is_hangul_char(result):
277 | return result
278 | raise InvalidJamoError("Could not synthesize characters to Hangul.",
279 | '\x00')
280 |
281 |
282 | def j2h(lead, vowel, tail=0):
283 | """Arguments may be integers corresponding to the U+11xx codepoints, the
284 | actual U+11xx jamo characters, or HCJ.
285 |
286 | Outputs a one-character Hangul string.
287 |
288 | This function is defined solely for naming conisistency with
289 | jamo_to_hangul.
290 | """
291 |
292 | return jamo_to_hangul(lead, vowel, tail)
293 |
294 |
295 | def synth_hangul(string):
296 | """Convert jamo characters in a string into hcj as much as possible."""
297 | raise NotImplementedError
298 | return ''.join([''.join(''.join(jamo_to_hcj(_)) for _ in string)])
299 |
--------------------------------------------------------------------------------
/audio_loader_for_gui.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import *
2 | from pydub import AudioSegment
3 |
4 |
5 | class WorkerSignals(QObject):
6 | result = pyqtSignal(object, object) # create a signal that gets an object as argument
7 |
8 |
9 | class myloader(QRunnable):
10 | def __init__(self, path, char):
11 | super().__init__()
12 | self.char = char
13 | self.path = path
14 | self.signals = WorkerSignals()
15 |
16 | @pyqtSlot()
17 | def run(self):
18 | data = AudioSegment.from_mp3(self.path)
19 | self.signals.result.emit(data, self.char)
20 |
--------------------------------------------------------------------------------
/audio_player_for_gui.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import QRunnable, pyqtSlot
2 | from pydub.playback import play
3 |
4 |
5 | class myplayer(QRunnable):
6 | def __init__(self, sound):
7 | super().__init__()
8 | self.sound = sound
9 |
10 | @pyqtSlot()
11 | def run(self):
12 | print('시작')
13 | play(self.sound)
14 | print('끝')
15 |
--------------------------------------------------------------------------------
/data/KoreanSyllableData/U+11xx.json:
--------------------------------------------------------------------------------
1 | {
2 | "\u1100": "HANGUL CHOSEONG KIYEOK",
3 | "\u1101": "HANGUL CHOSEONG SSANGKIYEOK",
4 | "\u1102": "HANGUL CHOSEONG NIEUN",
5 | "\u1103": "HANGUL CHOSEONG TIKEUT",
6 | "\u1104": "HANGUL CHOSEONG SSANGTIKEUT",
7 | "\u1105": "HANGUL CHOSEONG RIEUL",
8 | "\u1106": "HANGUL CHOSEONG MIEUM",
9 | "\u1107": "HANGUL CHOSEONG PIEUP",
10 | "\u1108": "HANGUL CHOSEONG SSANGPIEUP",
11 | "\u1109": "HANGUL CHOSEONG SIOS",
12 | "\u110a": "HANGUL CHOSEONG SSANGSIOS",
13 | "\u110b": "HANGUL CHOSEONG IEUNG",
14 | "\u110c": "HANGUL CHOSEONG CIEUC",
15 | "\u110d": "HANGUL CHOSEONG SSANGCIEUC",
16 | "\u110e": "HANGUL CHOSEONG CHIEUCH",
17 | "\u110f": "HANGUL CHOSEONG KHIEUKH",
18 | "\u1110": "HANGUL CHOSEONG THIEUTH",
19 | "\u1111": "HANGUL CHOSEONG PHIEUPH",
20 | "\u1112": "HANGUL CHOSEONG HIEUH",
21 | "\u1113": "HANGUL CHOSEONG NIEUN-KIYEOK",
22 | "\u1114": "HANGUL CHOSEONG SSANGNIEUN",
23 | "\u1115": "HANGUL CHOSEONG NIEUN-TIKEUT",
24 | "\u1116": "HANGUL CHOSEONG NIEUN-PIEUP",
25 | "\u1117": "HANGUL CHOSEONG TIKEUT-KIYEOK",
26 | "\u1118": "HANGUL CHOSEONG RIEUL-NIEUN",
27 | "\u1119": "HANGUL CHOSEONG SSANGRIEUL",
28 | "\u111a": "HANGUL CHOSEONG RIEUL-HIEUH",
29 | "\u111b": "HANGUL CHOSEONG KAPYEOUNRIEUL",
30 | "\u111c": "HANGUL CHOSEONG MIEUM-PIEUP",
31 | "\u111d": "HANGUL CHOSEONG KAPYEOUNMIEUM",
32 | "\u111e": "HANGUL CHOSEONG PIEUP-KIYEOK",
33 | "\u111f": "HANGUL CHOSEONG PIEUP-NIEUN",
34 | "\u1120": "HANGUL CHOSEONG PIEUP-TIKEUT",
35 | "\u1121": "HANGUL CHOSEONG PIEUP-SIOS",
36 | "\u1122": "HANGUL CHOSEONG PIEUP-SIOS-KIYEOK",
37 | "\u1123": "HANGUL CHOSEONG PIEUP-SIOS-TIKEUT",
38 | "\u1124": "HANGUL CHOSEONG PIEUP-SIOS-PIEUP",
39 | "\u1125": "HANGUL CHOSEONG PIEUP-SSANGSIOS",
40 | "\u1126": "HANGUL CHOSEONG PIEUP-SIOS-CIEUC",
41 | "\u1127": "HANGUL CHOSEONG PIEUP-CIEUC",
42 | "\u1128": "HANGUL CHOSEONG PIEUP-CHIEUCH",
43 | "\u1129": "HANGUL CHOSEONG PIEUP-THIEUTH",
44 | "\u112a": "HANGUL CHOSEONG PIEUP-PHIEUPH",
45 | "\u112b": "HANGUL CHOSEONG KAPYEOUNPIEUP",
46 | "\u112c": "HANGUL CHOSEONG KAPYEOUNSSANGPIEUP",
47 | "\u112d": "HANGUL CHOSEONG SIOS-KIYEOK",
48 | "\u112e": "HANGUL CHOSEONG SIOS-NIEUN",
49 | "\u112f": "HANGUL CHOSEONG SIOS-TIKEUT",
50 | "\u1130": "HANGUL CHOSEONG SIOS-RIEUL",
51 | "\u1131": "HANGUL CHOSEONG SIOS-MIEUM",
52 | "\u1132": "HANGUL CHOSEONG SIOS-PIEUP",
53 | "\u1133": "HANGUL CHOSEONG SIOS-PIEUP-KIYEOK",
54 | "\u1134": "HANGUL CHOSEONG SIOS-SSANGSIOS",
55 | "\u1135": "HANGUL CHOSEONG SIOS-IEUNG",
56 | "\u1136": "HANGUL CHOSEONG SIOS-CIEUC",
57 | "\u1137": "HANGUL CHOSEONG SIOS-CHIEUCH",
58 | "\u1138": "HANGUL CHOSEONG SIOS-KHIEUKH",
59 | "\u1139": "HANGUL CHOSEONG SIOS-THIEUTH",
60 | "\u113a": "HANGUL CHOSEONG SIOS-PHIEUPH",
61 | "\u113b": "HANGUL CHOSEONG SIOS-HIEUH",
62 | "\u113c": "HANGUL CHOSEONG CHITUEUMSIOS",
63 | "\u113d": "HANGUL CHOSEONG CHITUEUMSSANGSIOS",
64 | "\u113e": "HANGUL CHOSEONG CEONGCHIEUMSIOS",
65 | "\u113f": "HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS",
66 | "\u1140": "HANGUL CHOSEONG PANSIOS",
67 | "\u1141": "HANGUL CHOSEONG IEUNG-KIYEOK",
68 | "\u1142": "HANGUL CHOSEONG IEUNG-TIKEUT",
69 | "\u1143": "HANGUL CHOSEONG IEUNG-MIEUM",
70 | "\u1144": "HANGUL CHOSEONG IEUNG-PIEUP",
71 | "\u1145": "HANGUL CHOSEONG IEUNG-SIOS",
72 | "\u1146": "HANGUL CHOSEONG IEUNG-PANSIOS",
73 | "\u1147": "HANGUL CHOSEONG SSANGIEUNG",
74 | "\u1148": "HANGUL CHOSEONG IEUNG-CIEUC",
75 | "\u1149": "HANGUL CHOSEONG IEUNG-CHIEUCH",
76 | "\u114a": "HANGUL CHOSEONG IEUNG-THIEUTH",
77 | "\u114b": "HANGUL CHOSEONG IEUNG-PHIEUPH",
78 | "\u114c": "HANGUL CHOSEONG YESIEUNG",
79 | "\u114d": "HANGUL CHOSEONG CIEUC-IEUNG",
80 | "\u114e": "HANGUL CHOSEONG CHITUEUMCIEUC",
81 | "\u114f": "HANGUL CHOSEONG CHITUEUMSSANGCIEUC",
82 | "\u1150": "HANGUL CHOSEONG CEONGCHIEUMCIEUC",
83 | "\u1151": "HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC",
84 | "\u1152": "HANGUL CHOSEONG CHIEUCH-KHIEUKH",
85 | "\u1153": "HANGUL CHOSEONG CHIEUCH-HIEUH",
86 | "\u1154": "HANGUL CHOSEONG CHITUEUMCHIEUCH",
87 | "\u1155": "HANGUL CHOSEONG CEONGCHIEUMCHIEUCH",
88 | "\u1156": "HANGUL CHOSEONG PHIEUPH-PIEUP",
89 | "\u1157": "HANGUL CHOSEONG KAPYEOUNPHIEUPH",
90 | "\u1158": "HANGUL CHOSEONG SSANGHIEUH",
91 | "\u1159": "HANGUL CHOSEONG YEORINHIEUH",
92 | "\u115a": "HANGUL CHOSEONG KIYEOK-TIKEUT",
93 | "\u115b": "HANGUL CHOSEONG NIEUN-SIOS",
94 | "\u115c": "HANGUL CHOSEONG NIEUN-CIEUC",
95 | "\u115d": "HANGUL CHOSEONG NIEUN-HIEUH",
96 | "\u115e": "HANGUL CHOSEONG TIKEUT-RIEUL",
97 | "\u115f": "HANGUL CHOSEONG FILLER",
98 | "\u1160": "HANGUL JUNGSEONG FILLER",
99 | "\u1161": "HANGUL JUNGSEONG A",
100 | "\u1162": "HANGUL JUNGSEONG AE",
101 | "\u1163": "HANGUL JUNGSEONG YA",
102 | "\u1164": "HANGUL JUNGSEONG YAE",
103 | "\u1165": "HANGUL JUNGSEONG EO",
104 | "\u1166": "HANGUL JUNGSEONG E",
105 | "\u1167": "HANGUL JUNGSEONG YEO",
106 | "\u1168": "HANGUL JUNGSEONG YE",
107 | "\u1169": "HANGUL JUNGSEONG O",
108 | "\u116a": "HANGUL JUNGSEONG WA",
109 | "\u116b": "HANGUL JUNGSEONG WAE",
110 | "\u116c": "HANGUL JUNGSEONG OE",
111 | "\u116d": "HANGUL JUNGSEONG YO",
112 | "\u116e": "HANGUL JUNGSEONG U",
113 | "\u116f": "HANGUL JUNGSEONG WEO",
114 | "\u1170": "HANGUL JUNGSEONG WE",
115 | "\u1171": "HANGUL JUNGSEONG WI",
116 | "\u1172": "HANGUL JUNGSEONG YU",
117 | "\u1173": "HANGUL JUNGSEONG EU",
118 | "\u1174": "HANGUL JUNGSEONG YI",
119 | "\u1175": "HANGUL JUNGSEONG I",
120 | "\u1176": "HANGUL JUNGSEONG A-O",
121 | "\u1177": "HANGUL JUNGSEONG A-U",
122 | "\u1178": "HANGUL JUNGSEONG YA-O",
123 | "\u1179": "HANGUL JUNGSEONG YA-YO",
124 | "\u117a": "HANGUL JUNGSEONG EO-O",
125 | "\u117b": "HANGUL JUNGSEONG EO-U",
126 | "\u117c": "HANGUL JUNGSEONG EO-EU",
127 | "\u117d": "HANGUL JUNGSEONG YEO-O",
128 | "\u117e": "HANGUL JUNGSEONG YEO-U",
129 | "\u117f": "HANGUL JUNGSEONG O-EO",
130 | "\u1180": "HANGUL JUNGSEONG O-E",
131 | "\u1181": "HANGUL JUNGSEONG O-YE",
132 | "\u1182": "HANGUL JUNGSEONG O-O",
133 | "\u1183": "HANGUL JUNGSEONG O-U",
134 | "\u1184": "HANGUL JUNGSEONG YO-YA",
135 | "\u1185": "HANGUL JUNGSEONG YO-YAE",
136 | "\u1186": "HANGUL JUNGSEONG YO-YEO",
137 | "\u1187": "HANGUL JUNGSEONG YO-O",
138 | "\u1188": "HANGUL JUNGSEONG YO-I",
139 | "\u1189": "HANGUL JUNGSEONG U-A",
140 | "\u118a": "HANGUL JUNGSEONG U-AE",
141 | "\u118b": "HANGUL JUNGSEONG U-EO-EU",
142 | "\u118c": "HANGUL JUNGSEONG U-YE",
143 | "\u118d": "HANGUL JUNGSEONG U-U",
144 | "\u118e": "HANGUL JUNGSEONG YU-A",
145 | "\u118f": "HANGUL JUNGSEONG YU-EO",
146 | "\u1190": "HANGUL JUNGSEONG YU-E",
147 | "\u1191": "HANGUL JUNGSEONG YU-YEO",
148 | "\u1192": "HANGUL JUNGSEONG YU-YE",
149 | "\u1193": "HANGUL JUNGSEONG YU-U",
150 | "\u1194": "HANGUL JUNGSEONG YU-I",
151 | "\u1195": "HANGUL JUNGSEONG EU-U",
152 | "\u1196": "HANGUL JUNGSEONG EU-EU",
153 | "\u1197": "HANGUL JUNGSEONG YI-U",
154 | "\u1198": "HANGUL JUNGSEONG I-A",
155 | "\u1199": "HANGUL JUNGSEONG I-YA",
156 | "\u119a": "HANGUL JUNGSEONG I-O",
157 | "\u119b": "HANGUL JUNGSEONG I-U",
158 | "\u119c": "HANGUL JUNGSEONG I-EU",
159 | "\u119d": "HANGUL JUNGSEONG I-ARAEA",
160 | "\u119e": "HANGUL JUNGSEONG ARAEA",
161 | "\u119f": "HANGUL JUNGSEONG ARAEA-EO",
162 | "\u11a0": "HANGUL JUNGSEONG ARAEA-U",
163 | "\u11a1": "HANGUL JUNGSEONG ARAEA-I",
164 | "\u11a2": "HANGUL JUNGSEONG SSANGARAEA",
165 | "\u11a3": "HANGUL JUNGSEONG A-EU",
166 | "\u11a4": "HANGUL JUNGSEONG YA-U",
167 | "\u11a5": "HANGUL JUNGSEONG YEO-YA",
168 | "\u11a6": "HANGUL JUNGSEONG O-YA",
169 | "\u11a7": "HANGUL JUNGSEONG O-YAE",
170 | "\u11a8": "HANGUL JONGSEONG KIYEOK",
171 | "\u11a9": "HANGUL JONGSEONG SSANGKIYEOK",
172 | "\u11aa": "HANGUL JONGSEONG KIYEOK-SIOS",
173 | "\u11ab": "HANGUL JONGSEONG NIEUN",
174 | "\u11ac": "HANGUL JONGSEONG NIEUN-CIEUC",
175 | "\u11ad": "HANGUL JONGSEONG NIEUN-HIEUH",
176 | "\u11ae": "HANGUL JONGSEONG TIKEUT",
177 | "\u11af": "HANGUL JONGSEONG RIEUL",
178 | "\u11b0": "HANGUL JONGSEONG RIEUL-KIYEOK",
179 | "\u11b1": "HANGUL JONGSEONG RIEUL-MIEUM",
180 | "\u11b2": "HANGUL JONGSEONG RIEUL-PIEUP",
181 | "\u11b3": "HANGUL JONGSEONG RIEUL-SIOS",
182 | "\u11b4": "HANGUL JONGSEONG RIEUL-THIEUTH",
183 | "\u11b5": "HANGUL JONGSEONG RIEUL-PHIEUPH",
184 | "\u11b6": "HANGUL JONGSEONG RIEUL-HIEUH",
185 | "\u11b7": "HANGUL JONGSEONG MIEUM",
186 | "\u11b8": "HANGUL JONGSEONG PIEUP",
187 | "\u11b9": "HANGUL JONGSEONG PIEUP-SIOS",
188 | "\u11ba": "HANGUL JONGSEONG SIOS",
189 | "\u11bb": "HANGUL JONGSEONG SSANGSIOS",
190 | "\u11bc": "HANGUL JONGSEONG IEUNG",
191 | "\u11bd": "HANGUL JONGSEONG CIEUC",
192 | "\u11be": "HANGUL JONGSEONG CHIEUCH",
193 | "\u11bf": "HANGUL JONGSEONG KHIEUKH",
194 | "\u11c0": "HANGUL JONGSEONG THIEUTH",
195 | "\u11c1": "HANGUL JONGSEONG PHIEUPH",
196 | "\u11c2": "HANGUL JONGSEONG HIEUH",
197 | "\u11c3": "HANGUL JONGSEONG KIYEOK-RIEUL",
198 | "\u11c4": "HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK",
199 | "\u11c5": "HANGUL JONGSEONG NIEUN-KIYEOK",
200 | "\u11c6": "HANGUL JONGSEONG NIEUN-TIKEUT",
201 | "\u11c7": "HANGUL JONGSEONG NIEUN-SIOS",
202 | "\u11c8": "HANGUL JONGSEONG NIEUN-PANSIOS",
203 | "\u11c9": "HANGUL JONGSEONG NIEUN-THIEUTH",
204 | "\u11ca": "HANGUL JONGSEONG TIKEUT-KIYEOK",
205 | "\u11cb": "HANGUL JONGSEONG TIKEUT-RIEUL",
206 | "\u11cc": "HANGUL JONGSEONG RIEUL-KIYEOK-SIOS",
207 | "\u11cd": "HANGUL JONGSEONG RIEUL-NIEUN",
208 | "\u11ce": "HANGUL JONGSEONG RIEUL-TIKEUT",
209 | "\u11cf": "HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH",
210 | "\u11d0": "HANGUL JONGSEONG SSANGRIEUL",
211 | "\u11d1": "HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK",
212 | "\u11d2": "HANGUL JONGSEONG RIEUL-MIEUM-SIOS",
213 | "\u11d3": "HANGUL JONGSEONG RIEUL-PIEUP-SIOS",
214 | "\u11d4": "HANGUL JONGSEONG RIEUL-PIEUP-HIEUH",
215 | "\u11d5": "HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP",
216 | "\u11d6": "HANGUL JONGSEONG RIEUL-SSANGSIOS",
217 | "\u11d7": "HANGUL JONGSEONG RIEUL-PANSIOS",
218 | "\u11d8": "HANGUL JONGSEONG RIEUL-KHIEUKH",
219 | "\u11d9": "HANGUL JONGSEONG RIEUL-YEORINHIEUH",
220 | "\u11da": "HANGUL JONGSEONG MIEUM-KIYEOK",
221 | "\u11db": "HANGUL JONGSEONG MIEUM-RIEUL",
222 | "\u11dc": "HANGUL JONGSEONG MIEUM-PIEUP",
223 | "\u11dd": "HANGUL JONGSEONG MIEUM-SIOS",
224 | "\u11de": "HANGUL JONGSEONG MIEUM-SSANGSIOS",
225 | "\u11df": "HANGUL JONGSEONG MIEUM-PANSIOS",
226 | "\u11e0": "HANGUL JONGSEONG MIEUM-CHIEUCH",
227 | "\u11e1": "HANGUL JONGSEONG MIEUM-HIEUH",
228 | "\u11e2": "HANGUL JONGSEONG KAPYEOUNMIEUM",
229 | "\u11e3": "HANGUL JONGSEONG PIEUP-RIEUL",
230 | "\u11e4": "HANGUL JONGSEONG PIEUP-PHIEUPH",
231 | "\u11e5": "HANGUL JONGSEONG PIEUP-HIEUH",
232 | "\u11e6": "HANGUL JONGSEONG KAPYEOUNPIEUP",
233 | "\u11e7": "HANGUL JONGSEONG SIOS-KIYEOK",
234 | "\u11e8": "HANGUL JONGSEONG SIOS-TIKEUT",
235 | "\u11e9": "HANGUL JONGSEONG SIOS-RIEUL",
236 | "\u11ea": "HANGUL JONGSEONG SIOS-PIEUP",
237 | "\u11eb": "HANGUL JONGSEONG PANSIOS",
238 | "\u11ec": "HANGUL JONGSEONG IEUNG-KIYEOK",
239 | "\u11ed": "HANGUL JONGSEONG IEUNG-SSANGKIYEOK",
240 | "\u11ee": "HANGUL JONGSEONG SSANGIEUNG",
241 | "\u11ef": "HANGUL JONGSEONG IEUNG-KHIEUKH",
242 | "\u11f0": "HANGUL JONGSEONG YESIEUNG",
243 | "\u11f1": "HANGUL JONGSEONG YESIEUNG-SIOS",
244 | "\u11f2": "HANGUL JONGSEONG YESIEUNG-PANSIOS",
245 | "\u11f3": "HANGUL JONGSEONG PHIEUPH-PIEUP",
246 | "\u11f4": "HANGUL JONGSEONG KAPYEOUNPHIEUPH",
247 | "\u11f5": "HANGUL JONGSEONG HIEUH-NIEUN",
248 | "\u11f6": "HANGUL JONGSEONG HIEUH-RIEUL",
249 | "\u11f7": "HANGUL JONGSEONG HIEUH-MIEUM",
250 | "\u11f8": "HANGUL JONGSEONG HIEUH-PIEUP",
251 | "\u11f9": "HANGUL JONGSEONG YEORINHIEUH",
252 | "\u11fa": "HANGUL JONGSEONG KIYEOK-NIEUN",
253 | "\u11fb": "HANGUL JONGSEONG KIYEOK-PIEUP",
254 | "\u11fc": "HANGUL JONGSEONG KIYEOK-CHIEUCH",
255 | "\u11fd": "HANGUL JONGSEONG KIYEOK-KHIEUKH",
256 | "\u11fe": "HANGUL JONGSEONG KIYEOK-HIEUH",
257 | "\u11ff": "HANGUL JONGSEONG SSANGNIEUN"
258 | }
--------------------------------------------------------------------------------
/data/KoreanSyllableData/U+31xx.json:
--------------------------------------------------------------------------------
1 | {
2 | "\u3131": "HANGUL LETTER KIYEOK",
3 | "\u3132": "HANGUL LETTER SSANGKIYEOK",
4 | "\u3133": "HANGUL LETTER KIYEOK-SIOS",
5 | "\u3134": "HANGUL LETTER NIEUN",
6 | "\u3135": "HANGUL LETTER NIEUN-CIEUC",
7 | "\u3136": "HANGUL LETTER NIEUN-HIEUH",
8 | "\u3137": "HANGUL LETTER TIKEUT",
9 | "\u3138": "HANGUL LETTER SSANGTIKEUT",
10 | "\u3139": "HANGUL LETTER RIEUL",
11 | "\u313a": "HANGUL LETTER RIEUL-KIYEOK",
12 | "\u313b": "HANGUL LETTER RIEUL-MIEUM",
13 | "\u313c": "HANGUL LETTER RIEUL-PIEUP",
14 | "\u313d": "HANGUL LETTER RIEUL-SIOS",
15 | "\u313e": "HANGUL LETTER RIEUL-THIEUTH",
16 | "\u313f": "HANGUL LETTER RIEUL-PHIEUPH",
17 | "\u3140": "HANGUL LETTER RIEUL-HIEUH",
18 | "\u3141": "HANGUL LETTER MIEUM",
19 | "\u3142": "HANGUL LETTER PIEUP",
20 | "\u3143": "HANGUL LETTER SSANGPIEUP",
21 | "\u3144": "HANGUL LETTER PIEUP-SIOS",
22 | "\u3145": "HANGUL LETTER SIOS",
23 | "\u3146": "HANGUL LETTER SSANGSIOS",
24 | "\u3147": "HANGUL LETTER IEUNG",
25 | "\u3148": "HANGUL LETTER CIEUC",
26 | "\u3149": "HANGUL LETTER SSANGCIEUC",
27 | "\u314a": "HANGUL LETTER CHIEUCH",
28 | "\u314b": "HANGUL LETTER KHIEUKH",
29 | "\u314c": "HANGUL LETTER THIEUTH",
30 | "\u314d": "HANGUL LETTER PHIEUPH",
31 | "\u314e": "HANGUL LETTER HIEUH",
32 | "\u314f": "HANGUL LETTER A",
33 | "\u3150": "HANGUL LETTER AE",
34 | "\u3151": "HANGUL LETTER YA",
35 | "\u3152": "HANGUL LETTER YAE",
36 | "\u3153": "HANGUL LETTER EO",
37 | "\u3154": "HANGUL LETTER E",
38 | "\u3155": "HANGUL LETTER YEO",
39 | "\u3156": "HANGUL LETTER YE",
40 | "\u3157": "HANGUL LETTER O",
41 | "\u3158": "HANGUL LETTER WA",
42 | "\u3159": "HANGUL LETTER WAE",
43 | "\u315a": "HANGUL LETTER OE",
44 | "\u315b": "HANGUL LETTER YO",
45 | "\u315c": "HANGUL LETTER U",
46 | "\u315d": "HANGUL LETTER WEO",
47 | "\u315e": "HANGUL LETTER WE",
48 | "\u315f": "HANGUL LETTER WI",
49 | "\u3160": "HANGUL LETTER YU",
50 | "\u3161": "HANGUL LETTER EU",
51 | "\u3162": "HANGUL LETTER YI",
52 | "\u3163": "HANGUL LETTER I",
53 | "\u3164": "HANGUL FILLER",
54 | "\u3165": "HANGUL LETTER SSANGNIEUN",
55 | "\u3166": "HANGUL LETTER NIEUN-TIKEUT",
56 | "\u3167": "HANGUL LETTER NIEUN-SIOS",
57 | "\u3168": "HANGUL LETTER NIEUN-PANSIOS",
58 | "\u3169": "HANGUL LETTER RIEUL-KIYEOK-SIOS",
59 | "\u316a": "HANGUL LETTER RIEUL-TIKEUT",
60 | "\u316b": "HANGUL LETTER RIEUL-PIEUP-SIOS",
61 | "\u316c": "HANGUL LETTER RIEUL-PANSIOS",
62 | "\u316d": "HANGUL LETTER RIEUL-YEORINHIEUH",
63 | "\u316e": "HANGUL LETTER MIEUM-PIEUP",
64 | "\u316f": "HANGUL LETTER MIEUM-SIOS",
65 | "\u3170": "HANGUL LETTER MIEUM-PANSIOS",
66 | "\u3171": "HANGUL LETTER KAPYEOUNMIEUM",
67 | "\u3172": "HANGUL LETTER PIEUP-KIYEOK",
68 | "\u3173": "HANGUL LETTER PIEUP-TIKEUT",
69 | "\u3174": "HANGUL LETTER PIEUP-SIOS-KIYEOK",
70 | "\u3175": "HANGUL LETTER PIEUP-SIOS-TIKEUT",
71 | "\u3176": "HANGUL LETTER PIEUP-CIEUC",
72 | "\u3177": "HANGUL LETTER PIEUP-THIEUTH",
73 | "\u3178": "HANGUL LETTER KAPYEOUNPIEUP",
74 | "\u3179": "HANGUL LETTER KAPYEOUNSSANGPIEUP",
75 | "\u317a": "HANGUL LETTER SIOS-KIYEOK",
76 | "\u317b": "HANGUL LETTER SIOS-NIEUN",
77 | "\u317c": "HANGUL LETTER SIOS-TIKEUT",
78 | "\u317d": "HANGUL LETTER SIOS-PIEUP",
79 | "\u317e": "HANGUL LETTER SIOS-CIEUC",
80 | "\u317f": "HANGUL LETTER PANSIOS",
81 | "\u3180": "HANGUL LETTER SSANGIEUNG",
82 | "\u3181": "HANGUL LETTER YESIEUNG",
83 | "\u3182": "HANGUL LETTER YESIEUNG-SIOS",
84 | "\u3183": "HANGUL LETTER YESIEUNG-PANSIOS",
85 | "\u3184": "HANGUL LETTER KAPYEOUNPHIEUPH",
86 | "\u3185": "HANGUL LETTER SSANGHIEUH",
87 | "\u3186": "HANGUL LETTER YEORINHIEUH",
88 | "\u3187": "HANGUL LETTER YO-YA",
89 | "\u3188": "HANGUL LETTER YO-YAE",
90 | "\u3189": "HANGUL LETTER YO-I",
91 | "\u318a": "HANGUL LETTER YU-YEO",
92 | "\u318b": "HANGUL LETTER YU-YE",
93 | "\u318c": "HANGUL LETTER YU-I",
94 | "\u318d": "HANGUL LETTER ARAEA",
95 | "\u318e": "HANGUL LETTER ARAEAE"
96 | }
--------------------------------------------------------------------------------
/pyanimalese_cli.py:
--------------------------------------------------------------------------------
1 | import random
2 | from pydub import AudioSegment
3 | from pydub.playback import play
4 | from jamo import h2j, j2hcj
5 |
6 | char_list = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ', ' ']
7 |
8 | char_sounds = {}
9 | char_sounds_high = {}
10 |
11 | for idx, item in enumerate(char_list):
12 | str_idx = str(idx + 1).zfill(2)
13 | char_sounds[item] = AudioSegment.from_mp3(f'./sources/{str_idx}.padata')
14 | char_sounds_high[item] = AudioSegment.from_mp3(f'./sources/high/{str_idx}.padata')
15 |
16 | while True:
17 | source = input('원본 문자열 입력: ')
18 |
19 | if source == "종료":
20 | break
21 |
22 | result_sound = None
23 | result_sound_high = None
24 | print('생성중...')
25 | for ch in source:
26 | jamo_ch = j2hcj(h2j(ch))
27 | if jamo_ch[0] not in char_list:
28 | print(f'지원되지 않는 문자를 건너뛰었습니다: {jamo_ch}')
29 | else:
30 | char_sound = char_sounds[jamo_ch[0]]
31 | char_sound_high = char_sounds_high[jamo_ch[0]]
32 |
33 | octaves = 2 * random.uniform(0.96, 1.15)
34 | new_sample_rate = int(char_sound.frame_rate * (2.0 ** octaves))
35 |
36 | pitch_char_sound = char_sound._spawn(char_sound.raw_data, overrides={'frame_rate': new_sample_rate})
37 | result_sound = pitch_char_sound if result_sound is None else result_sound + pitch_char_sound
38 |
39 | pitch_char_sound_high = char_sound_high._spawn(char_sound_high.raw_data,
40 | overrides={'frame_rate': new_sample_rate})
41 | result_sound_high = pitch_char_sound_high if result_sound_high is None else result_sound_high + pitch_char_sound_high
42 |
43 | print("재생중: " + source + "(일반)")
44 | play(result_sound)
45 |
46 | print("재생중: " + source + "(고음)")
47 | play(result_sound_high)
48 |
49 | print('종료되었습니다.')
50 |
--------------------------------------------------------------------------------
/pyanimalese_gui.py:
--------------------------------------------------------------------------------
1 | # Form implementation generated from reading ui file 'PyAnimalese.ui'
2 | #
3 | # Created by: PyQt6 UI code generator 6.3.0
4 | #
5 | # WARNING: Any manual changes made to this file will be lost when pyuic6 is
6 | # run again. Do not edit this file unless you know what you are doing.
7 | import os.path
8 | import traceback
9 | from PyQt6 import QtCore, QtGui, QtWidgets
10 | from PyQt6.QtCore import QThreadPool
11 | import random
12 | from pydub import AudioSegment
13 | from pydub.playback import play
14 | from audio_player_for_gui import myplayer
15 |
16 | from audio_loader_for_gui import myloader
17 |
18 | try:
19 | from jamo import h2j, j2hcj
20 | except:
21 | from __jamo import h2j, j2hcj
22 |
23 | is_debug = False
24 | else:
25 | is_debug = True
26 |
27 |
28 | class Ui_MainWindow(object):
29 | def setupUi(self, MainWindow):
30 | MainWindow.setObjectName("MainWindow")
31 | MainWindow.resize(700, 400)
32 | MainWindow.setMinimumSize(QtCore.QSize(700, 400))
33 | MainWindow.setMaximumSize(QtCore.QSize(700, 400))
34 | MainWindow.setAutoFillBackground(False)
35 | self.centralwidget = QtWidgets.QWidget(MainWindow)
36 | self.centralwidget.setObjectName("centralwidget")
37 | self.help_label = QtWidgets.QLabel(self.centralwidget)
38 | self.help_label.setGeometry(QtCore.QRect(20, 20, 331, 41))
39 | self.help_label.setObjectName("help_label")
40 | self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
41 | self.horizontalLayoutWidget.setGeometry(QtCore.QRect(20, 60, 531, 31))
42 | self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
43 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
44 | self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
45 | self.horizontalLayout.setObjectName("horizontalLayout")
46 | self.source_text = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
47 | self.source_text.setObjectName("source_text")
48 | self.horizontalLayout.addWidget(self.source_text)
49 | self.convert_btn = QtWidgets.QPushButton(self.horizontalLayoutWidget)
50 | self.convert_btn.setObjectName("convert_btn")
51 | self.horizontalLayout.addWidget(self.convert_btn)
52 | self.progress_label = QtWidgets.QLabel(self.centralwidget)
53 | self.progress_label.setGeometry(QtCore.QRect(20, 120, 531, 41))
54 | self.progress_label.setObjectName("progress_label")
55 | self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget)
56 | self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(20, 280, 451, 51))
57 | self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
58 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
59 | self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
60 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
61 | self.play_or_save_label = QtWidgets.QLabel(self.horizontalLayoutWidget_2)
62 | self.play_or_save_label.setObjectName("play_or_save_label")
63 | self.horizontalLayout_2.addWidget(self.play_or_save_label)
64 | self.play_btn = QtWidgets.QPushButton(self.horizontalLayoutWidget_2)
65 | self.play_btn.setObjectName("play_btn")
66 | self.horizontalLayout_2.addWidget(self.play_btn)
67 | self.save_btn = QtWidgets.QPushButton(self.horizontalLayoutWidget_2)
68 | self.save_btn.setObjectName("save_btn")
69 | self.horizontalLayout_2.addWidget(self.save_btn)
70 | # self.horizontalLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget)
71 | # self.horizontalLayoutWidget_3.setGeometry(QtCore.QRect(20, 310, 451, 41))
72 | # self.horizontalLayoutWidget_3.setObjectName("horizontalLayoutWidget_3")
73 | # self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_3)
74 | # self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
75 | # self.horizontalLayout_3.setObjectName("horizontalLayout_3")
76 | # self.set_path_btn = QtWidgets.QPushButton(self.horizontalLayoutWidget_3)
77 | # self.set_path_btn.setObjectName("set_path_btn")
78 | # self.horizontalLayout_3.addWidget(self.set_path_btn)
79 | # self.github_btn = QtWidgets.QPushButton(self.horizontalLayoutWidget_3)
80 | # self.github_btn.setObjectName("github_btn")
81 | # self.horizontalLayout_3.addWidget(self.github_btn)
82 | self.contact_label = QtWidgets.QLabel(self.centralwidget)
83 | self.contact_label.setGeometry(QtCore.QRect(20, 360, 341, 41))
84 | self.contact_label.setObjectName("contact_label")
85 | self.progress_bar = QtWidgets.QProgressBar(self.centralwidget)
86 | self.progress_bar.setGeometry(QtCore.QRect(20, 100, 531, 23))
87 | self.progress_bar.setProperty("value", 0)
88 | self.progress_bar.setObjectName("progress_bar")
89 | self.line = QtWidgets.QFrame(self.centralwidget)
90 | self.line.setGeometry(QtCore.QRect(20, 250, 531, 20))
91 | self.line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
92 | self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
93 | self.line.setObjectName("line")
94 | MainWindow.setCentralWidget(self.centralwidget)
95 |
96 | self.connect_events()
97 |
98 | self.retranslateUi(MainWindow)
99 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
100 |
101 | def retranslateUi(self, MainWindow):
102 | _translate = QtCore.QCoreApplication.translate
103 | MainWindow.setWindowTitle(_translate("MainWindow", f"PyAnimalese {self.version}"))
104 | self.help_label.setText(_translate("MainWindow", "동물의 숲 NPC 목소리로 읽고 싶은 문장을 입력하세요."))
105 | self.convert_btn.setText(_translate("MainWindow", "변환"))
106 | self.progress_label.setText(_translate("MainWindow", "대기중..."))
107 | self.play_or_save_label.setText(_translate("MainWindow", "생성된 음성을..."))
108 | self.play_btn.setText(_translate("MainWindow", "재생하기"))
109 | self.save_btn.setText(_translate("MainWindow", "저장하기"))
110 | # self.set_path_btn.setText(_translate("MainWindow", "저장경로 설정하기"))
111 | # self.github_btn.setText(_translate("MainWindow", "GitHub Repo 확인하기"))
112 | self.contact_label.setText(_translate("MainWindow", "개발자: 주휘중 (contact@jbstudio.xyz)"))
113 |
114 | # My codes below
115 | def __init__(self):
116 | self.version = "1.0.0"
117 | self.threadpool = QThreadPool()
118 | self.root_dir = '\\'.join(sys.executable.split('\\')[0:-1])
119 | self.is_processing = False
120 | self.result_sound = None
121 | self.source_string = ""
122 | self.char_list = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ', ' ']
123 |
124 | self.char_sounds = {}
125 | # self.char_sounds_high = {}
126 | self.loading_audio_files = False
127 | self.loaded_audio_num = 0
128 |
129 | def connect_events(self):
130 | self.convert_btn.clicked.connect(self.convert_to_animalese)
131 | self.play_btn.clicked.connect(self.play_sound)
132 | self.save_btn.clicked.connect(lambda: self.save_sound(self.source_string))
133 |
134 | def initialize_audio_files(self):
135 | if self.loading_audio_files:
136 | return
137 |
138 | self.loading_audio_files = True
139 | self.progress_label.setText(f"데이터 파일을 불러오고 있어요. 검은색 창이 나와도 놀라지 마세요! ({self.loaded_audio_num}/{len(self.char_list)})")
140 | QtWidgets.QApplication.processEvents()
141 |
142 | for idx, item in enumerate(self.char_list):
143 | str_idx = str(idx + 1).zfill(2)
144 | if is_debug:
145 | try:
146 | my_loader = myloader(f'./sources/{str_idx}.padata', item)
147 | my_loader.signals.result.connect(self.finished_single_audio_file)
148 | self.threadpool.start(my_loader)
149 | except Exception as e:
150 | print(e)
151 |
152 | # self.char_sounds[item] = AudioSegment.from_mp3(f'{self.root_dir}\\data\\sources\\{str_idx}.padata')
153 | # self.char_sounds_high[item] = f'./sources/high/{str_idx}.padata'
154 | else:
155 | my_loader = myloader(f'{self.root_dir}\\data\\sources\\{str_idx}.padata', item)
156 | my_loader.signals.result.connect(self.finished_single_audio_file)
157 | self.threadpool.start(my_loader)
158 | # self.char_sounds[item] = AudioSegment.from_mp3(f'{self.root_dir}\\data\\sources\\{str_idx}.padata')
159 |
160 | def finished_single_audio_file(self, sound, char):
161 | self.char_sounds[char] = sound
162 |
163 | self.loaded_audio_num += 1
164 | self.progress_bar.setMinimum(0)
165 | self.progress_bar.setMaximum(len(self.char_list))
166 | self.progress_bar.value = self.loaded_audio_num
167 | print( self.loaded_audio_num)
168 | self.progress_label.setText(f"데이터 파일을 불러오고 있어요. 검은색 창이 나와도 놀라지 마세요! ({self.loaded_audio_num}/{len(self.char_list)})")
169 | QtWidgets.QApplication.processEvents()
170 |
171 | if len(self.char_sounds) == len(self.char_list):
172 | print('완료')
173 | self.loading_audio_files = False
174 | self.progress_label.setText("로드 완료")
175 | QtWidgets.QApplication.processEvents()
176 |
177 | def convert_to_animalese(self):
178 | if self.is_processing:
179 | return
180 |
181 | if self.loading_audio_files:
182 | return
183 |
184 | self.source_string = self.source_text.text()
185 | self.result_sound = None
186 | # print(self.source_string)
187 | self.is_processing = True
188 | self.progress_label.setText("변환중...")
189 | self.progress_bar.setMinimum(0)
190 | self.progress_bar.setMaximum(len(self.source_string))
191 | QtWidgets.QApplication.processEvents()
192 |
193 | completed_char_num = 0
194 | skipped_char_num = 0
195 | for ch in self.source_string:
196 | jamo_ch = j2hcj(h2j(ch))
197 | # print(jamo_ch)
198 | if jamo_ch[0] not in self.char_list:
199 | skipped_char_num += 1
200 | else:
201 | char_sound = self.char_sounds[jamo_ch[0]]
202 |
203 | octaves = 2 * random.uniform(0.96, 1.15)
204 | new_sample_rate = int(char_sound.frame_rate * (2.0 ** octaves))
205 |
206 | pitch_char_sound = char_sound._spawn(char_sound.raw_data, overrides={'frame_rate': new_sample_rate})
207 | self.result_sound = pitch_char_sound if self.result_sound is None else self.result_sound + pitch_char_sound
208 |
209 | completed_char_num += 1
210 | self.progress_bar.setValue(completed_char_num)
211 |
212 | self.is_processing = False
213 | self.progress_label.setText("변환완료")
214 | QtWidgets.QApplication.processEvents()
215 |
216 | self.play_sound()
217 |
218 | def play_sound(self):
219 | if self.result_sound is None:
220 | return
221 |
222 | my_player = myplayer(self.result_sound)
223 | self.threadpool.start(my_player)
224 |
225 | def save_sound(self, filename):
226 | if self.result_sound is None:
227 | return
228 |
229 | try:
230 | os.makedirs('result', exist_ok=True)
231 | self.result_sound.export(f'{self.root_dir}\\result\\{self.source_string}.mp3', format='mp3')
232 | except Exception as e:
233 | traceback.print_exc()
234 |
235 |
236 | if __name__ == "__main__":
237 | import sys
238 |
239 | app = QtWidgets.QApplication(sys.argv)
240 | MainWindow = QtWidgets.QMainWindow()
241 | ui = Ui_MainWindow()
242 | ui.setupUi(MainWindow)
243 | MainWindow.show()
244 | ui.initialize_audio_files()
245 | sys.exit(app.exec())
246 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/requirements.txt
--------------------------------------------------------------------------------
/sources/01.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/01.padata
--------------------------------------------------------------------------------
/sources/02.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/02.padata
--------------------------------------------------------------------------------
/sources/03.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/03.padata
--------------------------------------------------------------------------------
/sources/04.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/04.padata
--------------------------------------------------------------------------------
/sources/05.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/05.padata
--------------------------------------------------------------------------------
/sources/06.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/06.padata
--------------------------------------------------------------------------------
/sources/07.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/07.padata
--------------------------------------------------------------------------------
/sources/08.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/08.padata
--------------------------------------------------------------------------------
/sources/09.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/09.padata
--------------------------------------------------------------------------------
/sources/10.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/10.padata
--------------------------------------------------------------------------------
/sources/11.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/11.padata
--------------------------------------------------------------------------------
/sources/12.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/12.padata
--------------------------------------------------------------------------------
/sources/13.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/13.padata
--------------------------------------------------------------------------------
/sources/14.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/14.padata
--------------------------------------------------------------------------------
/sources/15.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/15.padata
--------------------------------------------------------------------------------
/sources/16.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/16.padata
--------------------------------------------------------------------------------
/sources/17.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/17.padata
--------------------------------------------------------------------------------
/sources/18.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/18.padata
--------------------------------------------------------------------------------
/sources/19.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/19.padata
--------------------------------------------------------------------------------
/sources/20.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/20.padata
--------------------------------------------------------------------------------
/sources/high/01.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/01.padata
--------------------------------------------------------------------------------
/sources/high/02.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/02.padata
--------------------------------------------------------------------------------
/sources/high/03.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/03.padata
--------------------------------------------------------------------------------
/sources/high/04.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/04.padata
--------------------------------------------------------------------------------
/sources/high/05.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/05.padata
--------------------------------------------------------------------------------
/sources/high/06.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/06.padata
--------------------------------------------------------------------------------
/sources/high/07.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/07.padata
--------------------------------------------------------------------------------
/sources/high/08.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/08.padata
--------------------------------------------------------------------------------
/sources/high/09.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/09.padata
--------------------------------------------------------------------------------
/sources/high/10.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/10.padata
--------------------------------------------------------------------------------
/sources/high/11.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/11.padata
--------------------------------------------------------------------------------
/sources/high/12.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/12.padata
--------------------------------------------------------------------------------
/sources/high/13.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/13.padata
--------------------------------------------------------------------------------
/sources/high/14.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/14.padata
--------------------------------------------------------------------------------
/sources/high/15.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/15.padata
--------------------------------------------------------------------------------
/sources/high/16.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/16.padata
--------------------------------------------------------------------------------
/sources/high/17.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/17.padata
--------------------------------------------------------------------------------
/sources/high/18.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/18.padata
--------------------------------------------------------------------------------
/sources/high/19.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/19.padata
--------------------------------------------------------------------------------
/sources/high/20.padata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hwi-middle/PyAnimalese/bbe29f5668791f3c678b4bdb72e4aa64b58b3e43/sources/high/20.padata
--------------------------------------------------------------------------------