morse2sound 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ Copyright 2024 Dr. Andreas Krüger, DJ3EI, dj3ei@famsik.de
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the “Software”), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.1
2
+ Name: morse2sound
3
+ Version: 1.0.1
4
+ Summary: Software to convert text into Morse code sound files.
5
+ Author-email: "Dr. Andreas Krüger, DJ3EI" <dj3ei@famsik.de>
6
+ Project-URL: Homepage, https://gitlab.com/4ham/morse2sound
7
+ Project-URL: Repository, https://gitlab.com/4ham/morse2sound
8
+ Project-URL: Issues, https://gitlab.com/4ham/morse2sound/-/issues
9
+ Keywords: Morse,Morsecode,ham,amateur radio,cw
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Intended Audience :: Education
17
+ Classifier: Topic :: Communications :: Ham Radio
18
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Sound Synthesis
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+
23
+ # Morse2sound
24
+
25
+ ## What can I do with this?
26
+
27
+ From some text, produce a sound file that contains that text, encoded
28
+ in Morse code.
29
+
30
+ ## Background
31
+
32
+ ### What's this Morse code thing?
33
+
34
+ Fast message transport via telegraphy was one of the first practical
35
+ uses of electricity, dating back to the middle of the 18th century.
36
+
37
+ [Samuel F. B. Morse](https://en.wikipedia.org/wiki/Samuel_Morse)
38
+ originally devised a scheme to transmit numbers via electric pulses,
39
+ with a code book that allowed translation of words to those numbers
40
+ and back. Later, his assistant [Alfred
41
+ L. Vail](https://en.wikipedia.org/wiki/Alfred_Vail) improved on this
42
+ by coding single letters, so the code book became obsolete. He used
43
+ short, long, longer, and extra long impulses. His original method is
44
+ still remembered as [American Morse
45
+ Code](https://en.wikipedia.org/wiki/American_Morse_code), although it
46
+ sees little use. This was again improved by [Friedrich
47
+ C. Gerke](https://en.wikipedia.org/wiki/Friedrich_Clemens_Gerke), who
48
+ simplified from four different impulse lengths to just two. Gerke's
49
+ code is virtually identical to what is nowadays known as International
50
+ Morse Code. In 1865, an international treaty established the
51
+ [ITU](https://www.itu.int/en/Pages/default.aspx) as the first
52
+ multinational standards organization, with the purpose of facilitating
53
+ cross-border transport of messages. That same [1865
54
+ treaty](http://handle.itu.int/11.1004/020.1000/5.1.61.fr.200) also
55
+ formalized Gerke's version of the Morse code. The coding of the
56
+ letters a-z and the digits 0-9 have remained unchanged since then, but
57
+ other things like punctuation indeed have seen some changes. The ITU
58
+ still exists and to this day standardizes the [international Morse
59
+ code](https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf),
60
+ among many other things.
61
+
62
+ ### Is anybody still using Morse telegraphy?
63
+
64
+ Morse telegraphy is still extensively used by certain sections of the
65
+ HAM [amateur radio](https://en.wikipedia.org/wiki/Amateur_radio)
66
+ community. For obscure historical/technical reasons, Morse telegraphy
67
+ is generally designated as "CW".
68
+
69
+ CW is ubiquitous. On an everyday basis, any CW-capable short wave
70
+ receiver (such as [Twente Web-SDR](http://websdr.ewi.utwente.nl:8901/)
71
+ available on the internet) is likely to pick up CW signals if tuned
72
+ through the designated telegraphy ranges, e.g., 7000-7040 kHz. CW
73
+ sees extensive use among DXers (radio amateurs trying to reach rare
74
+ far-away places), in contests (radio amateur competitions), and by the
75
+ "home-brew" community (radio amateurs constructing their own
76
+ equipment). By its supporters, CW is considered an enjoyable mode of
77
+ operation.
78
+
79
+ ## Usage
80
+
81
+ ### What does this software do?
82
+
83
+ It takes text and produces, as a sound file or stream, a Morse encoded
84
+ version of that text.
85
+
86
+ The output format is that of
87
+ [`.wav`](https://en.wikipedia.org/wiki/WAV) files.
88
+
89
+ ### Basic usage
90
+
91
+ ```shell
92
+ morse2sound --in message.txt --wpm 12 --out message.wav
93
+ ```
94
+
95
+ ### Character set
96
+
97
+ The international Morse code.
98
+
99
+ ## Remarks
100
+
101
+ ### Compression
102
+
103
+ The `.wav` files generated compress quite well, with the compression
104
+ algorithm of your choice.
105
+
106
+ ## Installation
107
+
108
+ Make sure when you say `python`, then a version 3 Python comes up.
109
+ E.g., `python --version` should give a 3.8 or better version.
110
+
111
+ I suggest you create and activate a `venv`. On Linux:
112
+
113
+ python3 -m venv venv
114
+ . venv/bin/activate
115
+
116
+
117
+ ### Install
118
+
119
+ pip install morse2sound
120
+
121
+ ## Contributing
122
+
123
+ Have `git` installed.
124
+
125
+ ```shell
126
+ git clone https://gitlab.com/4ham/morse2sound.git
127
+ ```
128
+
129
+ Change to the directory `morse2sound` that was created by the above.
130
+
131
+ Create and activate a virtual environment as per the
132
+ [documentation](https://docs.python.org/3/library/venv.html#creating-virtual-environments)
133
+ pertaining to your OS.
134
+
135
+ Then:
136
+
137
+ ```shell
138
+ pip install -r requirements_test.frozen
139
+ ./lint.py
140
+ ```
141
+
142
+ Do your changes and repeat that `python ./lint.py` part in your branch
143
+ before starting a pull request as usual towards the
144
+ [repository](https://gitlab.com/4ham/morse2sound).
145
+
146
+ ## To run locally
147
+
148
+ ```shell
149
+ PYTHONPATH=$(pwd)/src python src/cw_enc/driver.py -h
150
+ ```
151
+
152
+ ## To build locally
153
+
154
+ In an activated
155
+ [virtual environment](https://docs.python.org/3/library/venv.html#creating-virtual-environments):
156
+
157
+ ```shell
158
+ pip install --upgrade build
159
+ rm -rf dist build morse2sound.egg-info
160
+ python -m build
161
+ ```
162
+
163
+ To install what you built:
164
+
165
+ ```shell
166
+ pip install dist/morse2sound-*-py3-none-any.whl
167
+ ```
168
+
169
+ ## To upload
170
+
171
+ This is useful only for the maintainer:
172
+
173
+ * Edit the version in `pyproject.toml`
174
+ * `git tag -s vx.y.z`
175
+ * `git push origin --tags`
176
+
177
+ ```shell
178
+ pip install --upgrade build twine
179
+ rm -rf dist build src/morse2sound.egg-info
180
+ python -m build
181
+ python -m twine upload dist/*
182
+ ```
@@ -0,0 +1,160 @@
1
+ # Morse2sound
2
+
3
+ ## What can I do with this?
4
+
5
+ From some text, produce a sound file that contains that text, encoded
6
+ in Morse code.
7
+
8
+ ## Background
9
+
10
+ ### What's this Morse code thing?
11
+
12
+ Fast message transport via telegraphy was one of the first practical
13
+ uses of electricity, dating back to the middle of the 18th century.
14
+
15
+ [Samuel F. B. Morse](https://en.wikipedia.org/wiki/Samuel_Morse)
16
+ originally devised a scheme to transmit numbers via electric pulses,
17
+ with a code book that allowed translation of words to those numbers
18
+ and back. Later, his assistant [Alfred
19
+ L. Vail](https://en.wikipedia.org/wiki/Alfred_Vail) improved on this
20
+ by coding single letters, so the code book became obsolete. He used
21
+ short, long, longer, and extra long impulses. His original method is
22
+ still remembered as [American Morse
23
+ Code](https://en.wikipedia.org/wiki/American_Morse_code), although it
24
+ sees little use. This was again improved by [Friedrich
25
+ C. Gerke](https://en.wikipedia.org/wiki/Friedrich_Clemens_Gerke), who
26
+ simplified from four different impulse lengths to just two. Gerke's
27
+ code is virtually identical to what is nowadays known as International
28
+ Morse Code. In 1865, an international treaty established the
29
+ [ITU](https://www.itu.int/en/Pages/default.aspx) as the first
30
+ multinational standards organization, with the purpose of facilitating
31
+ cross-border transport of messages. That same [1865
32
+ treaty](http://handle.itu.int/11.1004/020.1000/5.1.61.fr.200) also
33
+ formalized Gerke's version of the Morse code. The coding of the
34
+ letters a-z and the digits 0-9 have remained unchanged since then, but
35
+ other things like punctuation indeed have seen some changes. The ITU
36
+ still exists and to this day standardizes the [international Morse
37
+ code](https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf),
38
+ among many other things.
39
+
40
+ ### Is anybody still using Morse telegraphy?
41
+
42
+ Morse telegraphy is still extensively used by certain sections of the
43
+ HAM [amateur radio](https://en.wikipedia.org/wiki/Amateur_radio)
44
+ community. For obscure historical/technical reasons, Morse telegraphy
45
+ is generally designated as "CW".
46
+
47
+ CW is ubiquitous. On an everyday basis, any CW-capable short wave
48
+ receiver (such as [Twente Web-SDR](http://websdr.ewi.utwente.nl:8901/)
49
+ available on the internet) is likely to pick up CW signals if tuned
50
+ through the designated telegraphy ranges, e.g., 7000-7040 kHz. CW
51
+ sees extensive use among DXers (radio amateurs trying to reach rare
52
+ far-away places), in contests (radio amateur competitions), and by the
53
+ "home-brew" community (radio amateurs constructing their own
54
+ equipment). By its supporters, CW is considered an enjoyable mode of
55
+ operation.
56
+
57
+ ## Usage
58
+
59
+ ### What does this software do?
60
+
61
+ It takes text and produces, as a sound file or stream, a Morse encoded
62
+ version of that text.
63
+
64
+ The output format is that of
65
+ [`.wav`](https://en.wikipedia.org/wiki/WAV) files.
66
+
67
+ ### Basic usage
68
+
69
+ ```shell
70
+ morse2sound --in message.txt --wpm 12 --out message.wav
71
+ ```
72
+
73
+ ### Character set
74
+
75
+ The international Morse code.
76
+
77
+ ## Remarks
78
+
79
+ ### Compression
80
+
81
+ The `.wav` files generated compress quite well, with the compression
82
+ algorithm of your choice.
83
+
84
+ ## Installation
85
+
86
+ Make sure when you say `python`, then a version 3 Python comes up.
87
+ E.g., `python --version` should give a 3.8 or better version.
88
+
89
+ I suggest you create and activate a `venv`. On Linux:
90
+
91
+ python3 -m venv venv
92
+ . venv/bin/activate
93
+
94
+
95
+ ### Install
96
+
97
+ pip install morse2sound
98
+
99
+ ## Contributing
100
+
101
+ Have `git` installed.
102
+
103
+ ```shell
104
+ git clone https://gitlab.com/4ham/morse2sound.git
105
+ ```
106
+
107
+ Change to the directory `morse2sound` that was created by the above.
108
+
109
+ Create and activate a virtual environment as per the
110
+ [documentation](https://docs.python.org/3/library/venv.html#creating-virtual-environments)
111
+ pertaining to your OS.
112
+
113
+ Then:
114
+
115
+ ```shell
116
+ pip install -r requirements_test.frozen
117
+ ./lint.py
118
+ ```
119
+
120
+ Do your changes and repeat that `python ./lint.py` part in your branch
121
+ before starting a pull request as usual towards the
122
+ [repository](https://gitlab.com/4ham/morse2sound).
123
+
124
+ ## To run locally
125
+
126
+ ```shell
127
+ PYTHONPATH=$(pwd)/src python src/cw_enc/driver.py -h
128
+ ```
129
+
130
+ ## To build locally
131
+
132
+ In an activated
133
+ [virtual environment](https://docs.python.org/3/library/venv.html#creating-virtual-environments):
134
+
135
+ ```shell
136
+ pip install --upgrade build
137
+ rm -rf dist build morse2sound.egg-info
138
+ python -m build
139
+ ```
140
+
141
+ To install what you built:
142
+
143
+ ```shell
144
+ pip install dist/morse2sound-*-py3-none-any.whl
145
+ ```
146
+
147
+ ## To upload
148
+
149
+ This is useful only for the maintainer:
150
+
151
+ * Edit the version in `pyproject.toml`
152
+ * `git tag -s vx.y.z`
153
+ * `git push origin --tags`
154
+
155
+ ```shell
156
+ pip install --upgrade build twine
157
+ rm -rf dist build src/morse2sound.egg-info
158
+ python -m build
159
+ python -m twine upload dist/*
160
+ ```
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ authors = [
7
+ {name = "Dr. Andreas Krüger, DJ3EI", email = "dj3ei@famsik.de"},
8
+ ]
9
+ name = "morse2sound"
10
+ description = "Software to convert text into Morse code sound files."
11
+ readme = "README.md"
12
+ requires-python = ">=3.8"
13
+ keywords = ["Morse", "Morsecode", "ham", "amateur radio", "cw"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Development Status :: 4 - Beta",
19
+ "Environment :: Console",
20
+ "Intended Audience :: End Users/Desktop",
21
+ "Intended Audience :: Education",
22
+ "Topic :: Communications :: Ham Radio",
23
+ "Topic :: Multimedia :: Sound/Audio :: Sound Synthesis"
24
+ ]
25
+ version = "1.0.1"
26
+ dependencies = []
27
+
28
+ [project.urls]
29
+ Homepage = "https://gitlab.com/4ham/morse2sound"
30
+ Repository = "https://gitlab.com/4ham/morse2sound"
31
+ Issues = "https://gitlab.com/4ham/morse2sound/-/issues"
32
+
33
+ [project.scripts]
34
+ morse2sound = "cw_enc.driver:drive"
35
+
36
+ [tool.isort]
37
+ profile = "black"
38
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,131 @@
1
+ import sys
2
+ import wave
3
+ from types import TracebackType
4
+ from typing import IO, Literal, Optional, Type
5
+
6
+ from cw_enc.cw_coder import (
7
+ CWCoder,
8
+ ignore_char,
9
+ raise_on_notmorseable_char,
10
+ replace_char_with_questionmark,
11
+ )
12
+ from cw_enc.paddle_cache import PaddleCache
13
+ from cw_enc.paddle_mksnd import PaddleMksnd
14
+ from cw_enc.paddle_samplecount import PaddleSampleCount
15
+ from cw_enc.parameters import ParameterError, Parameters
16
+ from cw_enc.wave_blocks import make_wave_blocks
17
+
18
+
19
+ def cw_enc(
20
+ intext: str = "Hello, world!",
21
+ infile: Optional[str] = None,
22
+ on_unmorseable: str = "?",
23
+ out: str = "cwenc_out.wav",
24
+ samples_per_second: int = 8000,
25
+ amplitude: float | int = 2**15 - 2,
26
+ channels: Literal[1] | Literal[2] = 1,
27
+ wpm: int | float = 12.0,
28
+ farnsworth_wpm: None | int | float = None,
29
+ f: int | float = 700,
30
+ f_delta: int | float = 1,
31
+ skirt_time: float = 5e-3,
32
+ skirt_shape: Literal["RC", "L"] = "RC",
33
+ ) -> None:
34
+ """The main driver program to produce Morse code .wav - files from characters."""
35
+
36
+ if infile is not None and intext is not None:
37
+ raise ParameterError("intext (when infile is also set)", intext)
38
+ elif intext is not None:
39
+ text = intext
40
+ elif infile is not None:
41
+ with open(infile) as inf:
42
+ text = inf.read()
43
+ else:
44
+ raise FileNotFoundError(
45
+ f"No text specified to convert to sound. Try {sys.argv[0]} -h"
46
+ )
47
+
48
+ pms = Parameters(
49
+ samples_per_second=samples_per_second,
50
+ amplitude=amplitude,
51
+ channels=channels,
52
+ wpm=wpm,
53
+ farnsworth_wpm=farnsworth_wpm,
54
+ f=f,
55
+ f_delta=f_delta,
56
+ skirt_time=skirt_time,
57
+ skirt_shape=skirt_shape,
58
+ )
59
+
60
+ binout: str | IO[bytes]
61
+ if out == "-":
62
+ binout = sys.stdout.buffer
63
+ else:
64
+ binout = out
65
+
66
+ with wave.open(binout, mode="wb") as wavout:
67
+ wavout.setnchannels(pms.channels)
68
+ wavout.setsampwidth(2)
69
+ wavout.setframerate(pms.samples_per_second)
70
+
71
+ # We will have a "paddle" that is given dits, dahs, between-character spaces, and between-words spaces.
72
+
73
+ # Waveblock, containing pre-canned byte sequence for those four.
74
+ wb = make_wave_blocks(pms)
75
+
76
+ class WriteWavData:
77
+ """Glue code: A simple object that has a write() method expecting bytes."""
78
+
79
+ def write(self, b: bytes) -> None:
80
+ wavout.writeframesraw(b)
81
+
82
+ # The paddle that actually does the "heavy lifting", writing the data to the file.
83
+ paddle_write_data = PaddleMksnd(what_to_write=wb, consumer=WriteWavData())
84
+
85
+ # But first, deal with a complication: .wav files want to know how long they're going to be.
86
+ class WriteWavHeader(PaddleSampleCount):
87
+ """A subclass of Paddle that can count how many samples will be written."""
88
+
89
+ def __exit__(
90
+ self,
91
+ exc_type: Optional[Type[BaseException]],
92
+ exc_val: Optional[BaseException],
93
+ exc_tb: Optional[TracebackType],
94
+ ) -> Literal[False]:
95
+ # On close, write the number of samples to the wav file:
96
+ wavout.setnframes(self._count)
97
+ return super().__exit__(exc_type, exc_val, exc_tb)
98
+
99
+ paddle_write_header = WriteWavHeader(
100
+ dit_samples=int(len(wb.dit) / pms.channels / 2),
101
+ dah_samples=int(len(wb.dah) / pms.channels / 2),
102
+ ci_silence_samples=int(len(wb.csilence) / pms.channels / 2),
103
+ iw_silence_samples=int(len(wb.iwsilence) / pms.channels / 2),
104
+ )
105
+
106
+ # We go through the entire message twice:
107
+ # First time for sample counting, secondly for real writing of the data.
108
+ #
109
+ # This is what a PaddleCache can do for us:
110
+ # It patiently stores all the information given to it, until it is closed.
111
+ # When it is closed (as a context manager), it will replay that information
112
+ # on each of the paddles handed in, in order, one after the other.
113
+ with PaddleCache([paddle_write_header, paddle_write_data]) as paddle:
114
+ # Someone needs to convert the morse code to strings of dits and dahs
115
+ # and hand those over to the paddle.
116
+ # That is what a CWCoder does.
117
+ if on_unmorseable == "False":
118
+ unmorseable_character_strategy = raise_on_notmorseable_char
119
+ elif on_unmorseable == "?":
120
+ unmorseable_character_strategy = replace_char_with_questionmark
121
+ elif on_unmorseable == "x":
122
+ unmorseable_character_strategy = ignore_char
123
+ else:
124
+ raise ParameterError("on_unmorseable", on_unmorseable)
125
+ coder = CWCoder(paddle=paddle, on_missing=unmorseable_character_strategy)
126
+ for c in text:
127
+ coder.code(c)
128
+
129
+ # For good luck and stability:
130
+ if out == "-":
131
+ sys.stdout.buffer.flush()
@@ -0,0 +1,103 @@
1
+ from typing import Callable
2
+
3
+ from cw_enc.paddle import Paddle
4
+
5
+
6
+ def raise_on_notmorseable_char(char: str) -> str:
7
+ raise KeyError('Character "{}" not representable in Morse code.'.format(char))
8
+
9
+
10
+ def ignore_char(char: str) -> str:
11
+ return "x"
12
+
13
+
14
+ def replace_char_with_questionmark(char: str) -> str:
15
+ return "..--.."
16
+
17
+
18
+ class CWCoder:
19
+ """Internal class that knows how to call dit and dah and so on, given a message character."""
20
+
21
+ codelist = {
22
+ "a": ".-",
23
+ "b": "-...",
24
+ "c": "-.-.",
25
+ "d": "-..",
26
+ "e": ".",
27
+ "f": "..-.",
28
+ "g": "--.",
29
+ "h": "....",
30
+ "i": "..",
31
+ "j": ".---",
32
+ "k": "-.-",
33
+ "l": ".-..",
34
+ "m": "--",
35
+ "n": "-.",
36
+ "o": "---",
37
+ "p": ".--.",
38
+ "q": "--.-",
39
+ "r": ".-.",
40
+ "s": "...",
41
+ "t": "-",
42
+ "u": "..-",
43
+ "v": "...-",
44
+ "w": ".--",
45
+ "x": "-..-",
46
+ "y": "-.--",
47
+ "z": "--..",
48
+ "0": "-----",
49
+ "1": ".----",
50
+ "2": "..---",
51
+ "3": "...--",
52
+ "4": "....-",
53
+ "5": ".....",
54
+ "6": "-....",
55
+ "7": "--...",
56
+ "8": "---..",
57
+ "9": "----.",
58
+ ":": "---...",
59
+ "=": "-...-",
60
+ "-": "-....-",
61
+ "?": "..--..",
62
+ ".": ".-.-.-",
63
+ ",": "--..--",
64
+ "(": "-.--.",
65
+ ")": "-.--.-",
66
+ "+": ".-.-.",
67
+ "/": "-..-.",
68
+ "@": ".--.-.",
69
+ }
70
+
71
+ def __init__(
72
+ self,
73
+ paddle: Paddle,
74
+ on_missing: Callable[[str], str] = raise_on_notmorseable_char,
75
+ codelist: dict[str, str] = codelist,
76
+ ) -> None:
77
+ self.paddle = paddle
78
+
79
+ class Codelist(dict[str, str]):
80
+ def __missing__(self, char: str) -> str:
81
+ return on_missing(char)
82
+
83
+ self.codelist = Codelist(codelist)
84
+
85
+ def code(self, char: str, csilence: bool = True) -> None:
86
+ if char == " " or char == "\n" or char == "\t":
87
+ self.paddle.iwsilence()
88
+ else:
89
+ code = self.codelist[char.lower()]
90
+ if code == "x":
91
+ pass
92
+ else:
93
+ for dida in self.codelist[char.lower()]:
94
+ if dida == ".":
95
+ self.paddle.dit()
96
+ elif dida == "-":
97
+ self.paddle.dah()
98
+ else:
99
+ raise RuntimeError(
100
+ 'Morse symbol "{}" is neither "." nor "-".'.format(dida)
101
+ )
102
+ if csilence:
103
+ self.paddle.csilence()