not1mm 24.4.30__py3-none-any.whl → 24.5.1__py3-none-any.whl
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.
- not1mm/__main__.py +22 -57
- not1mm/lib/version.py +1 -1
- not1mm/voice_keying.py +100 -0
- {not1mm-24.4.30.dist-info → not1mm-24.5.1.dist-info}/METADATA +2 -19
- {not1mm-24.4.30.dist-info → not1mm-24.5.1.dist-info}/RECORD +9 -8
- {not1mm-24.4.30.dist-info → not1mm-24.5.1.dist-info}/LICENSE +0 -0
- {not1mm-24.4.30.dist-info → not1mm-24.5.1.dist-info}/WHEEL +0 -0
- {not1mm-24.4.30.dist-info → not1mm-24.5.1.dist-info}/entry_points.txt +0 -0
- {not1mm-24.4.30.dist-info → not1mm-24.5.1.dist-info}/top_level.txt +0 -0
not1mm/__main__.py
CHANGED
@@ -35,7 +35,6 @@ except OSError as exception:
|
|
35
35
|
print(exception)
|
36
36
|
print("portaudio is not installed")
|
37
37
|
sd = None
|
38
|
-
import soundfile as sf
|
39
38
|
from PyQt6 import QtCore, QtGui, QtWidgets, uic
|
40
39
|
from PyQt6.QtCore import QDir, Qt, QThread
|
41
40
|
from PyQt6.QtGui import QFontDatabase, QColorConstants, QPalette, QColor
|
@@ -75,6 +74,7 @@ from not1mm.checkwindow import CheckWindow
|
|
75
74
|
from not1mm.bandmap import BandMapWindow
|
76
75
|
from not1mm.vfo import VfoWindow
|
77
76
|
from not1mm.radio import Radio
|
77
|
+
from not1mm.voice_keying import Voice
|
78
78
|
|
79
79
|
poll_time = datetime.datetime.now()
|
80
80
|
|
@@ -188,6 +188,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
188
188
|
current_palette = None
|
189
189
|
|
190
190
|
radio_thread = QThread()
|
191
|
+
voice_thread = QThread()
|
191
192
|
|
192
193
|
rig_control = None
|
193
194
|
log_window = None
|
@@ -479,6 +480,18 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
479
480
|
with open(fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8") as c_file:
|
480
481
|
self.ctyfile = loads(c_file.read())
|
481
482
|
self.readpreferences()
|
483
|
+
|
484
|
+
self.voice_process = Voice()
|
485
|
+
self.voice_process.moveToThread(self.voice_thread)
|
486
|
+
self.voice_thread.started.connect(self.voice_process.run)
|
487
|
+
self.voice_thread.finished.connect(self.voice_process.deleteLater)
|
488
|
+
self.voice_process.ptt_on.connect(self.ptt_on)
|
489
|
+
self.voice_process.ptt_off.connect(self.ptt_off)
|
490
|
+
self.voice_process.current_op = self.current_op
|
491
|
+
self.voice_process.data_path = fsutils.USER_DATA_PATH
|
492
|
+
self.voice_process.sounddevice = self.pref.get("sounddevice", "default")
|
493
|
+
self.voice_thread.start()
|
494
|
+
|
482
495
|
self.dbname = fsutils.USER_DATA_PATH / self.pref.get(
|
483
496
|
"current_database", "ham.db"
|
484
497
|
)
|
@@ -492,6 +505,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
492
505
|
self.station = {}
|
493
506
|
self.contact = self.database.empty_contact
|
494
507
|
self.current_op = self.station.get("Call", "")
|
508
|
+
self.voice_process.current_op = self.current_op
|
495
509
|
self.make_op_dir()
|
496
510
|
self.read_cw_macros()
|
497
511
|
self.clearinputs()
|
@@ -922,6 +936,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
922
936
|
if self.station is None:
|
923
937
|
self.station = {}
|
924
938
|
self.current_op = self.station.get("Call", "")
|
939
|
+
self.voice_process.current_op = self.current_op
|
925
940
|
self.make_op_dir()
|
926
941
|
cmd = {}
|
927
942
|
cmd["cmd"] = "NEWDB"
|
@@ -958,6 +973,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
958
973
|
if self.station.get("Call", "") == "":
|
959
974
|
self.edit_station_settings()
|
960
975
|
self.current_op = self.station.get("Call", "")
|
976
|
+
self.voice_process.current_op = self.current_op
|
961
977
|
self.make_op_dir()
|
962
978
|
cmd = {}
|
963
979
|
cmd["cmd"] = "NEWDB"
|
@@ -2118,6 +2134,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2118
2134
|
self.settings_dialog.close()
|
2119
2135
|
if self.current_op == "":
|
2120
2136
|
self.current_op = self.station.get("Call", "")
|
2137
|
+
self.voice_process.current_op = self.current_op
|
2121
2138
|
self.make_op_dir()
|
2122
2139
|
contest_count = self.database.fetch_all_contests()
|
2123
2140
|
if len(contest_count) == 0:
|
@@ -2199,61 +2216,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2199
2216
|
)
|
2200
2217
|
return macro
|
2201
2218
|
|
2202
|
-
def voice_string(self, the_string: str) -> None:
|
2203
|
-
"""
|
2204
|
-
voices string using nato phonetics.
|
2205
|
-
|
2206
|
-
Parameters
|
2207
|
-
----------
|
2208
|
-
the_string : str
|
2209
|
-
String to voicify.
|
2210
|
-
|
2211
|
-
Returns
|
2212
|
-
-------
|
2213
|
-
None
|
2214
|
-
"""
|
2215
|
-
|
2216
|
-
logger.debug("Voicing: %s", the_string)
|
2217
|
-
if sd is None:
|
2218
|
-
logger.warning("Sounddevice/portaudio not installed.")
|
2219
|
-
return
|
2220
|
-
op_path = fsutils.USER_DATA_PATH / self.current_op
|
2221
|
-
if "[" in the_string:
|
2222
|
-
sub_string = the_string.strip("[]").lower()
|
2223
|
-
filename = f"{str(op_path)}/{sub_string}.wav"
|
2224
|
-
if Path(filename).is_file():
|
2225
|
-
logger.debug("Voicing: %s", filename)
|
2226
|
-
data, _fs = sf.read(filename, dtype="float32")
|
2227
|
-
self.ptt_on()
|
2228
|
-
try:
|
2229
|
-
sd.default.device = self.pref.get("sounddevice", "default")
|
2230
|
-
sd.default.samplerate = 44100.0
|
2231
|
-
sd.play(data, blocking=False)
|
2232
|
-
# _status = sd.wait()
|
2233
|
-
# https://snyk.io/advisor/python/sounddevice/functions/sounddevice.PortAudioError
|
2234
|
-
except sd.PortAudioError as err:
|
2235
|
-
logger.warning("%s", f"{err}")
|
2236
|
-
|
2237
|
-
self.ptt_off()
|
2238
|
-
return
|
2239
|
-
self.ptt_on()
|
2240
|
-
for letter in the_string.lower():
|
2241
|
-
if letter in "abcdefghijklmnopqrstuvwxyz 1234567890":
|
2242
|
-
if letter == " ":
|
2243
|
-
letter = "space"
|
2244
|
-
filename = f"{str(op_path)}/{letter}.wav"
|
2245
|
-
if Path(filename).is_file():
|
2246
|
-
logger.debug("Voicing: %s", filename)
|
2247
|
-
data, _fs = sf.read(filename, dtype="float32")
|
2248
|
-
try:
|
2249
|
-
sd.default.device = self.pref.get("sounddevice", "default")
|
2250
|
-
sd.default.samplerate = 44100.0
|
2251
|
-
sd.play(data, blocking=False)
|
2252
|
-
logger.debug("%s", f"{sd.wait()}")
|
2253
|
-
except sd.PortAudioError as err:
|
2254
|
-
logger.warning("%s", f"{err}")
|
2255
|
-
self.ptt_off()
|
2256
|
-
|
2257
2219
|
def ptt_on(self) -> None:
|
2258
2220
|
"""
|
2259
2221
|
Turn on ptt for rig.
|
@@ -2285,6 +2247,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2285
2247
|
-------
|
2286
2248
|
None
|
2287
2249
|
"""
|
2250
|
+
|
2288
2251
|
logger.debug("PTT Off")
|
2289
2252
|
if self.rig_control:
|
2290
2253
|
self.leftdot.setPixmap(self.reddot)
|
@@ -2309,7 +2272,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2309
2272
|
if self.n1mm:
|
2310
2273
|
self.n1mm.radio_info["FunctionKeyCaption"] = function_key.text()
|
2311
2274
|
if self.radio_state.get("mode") in ["LSB", "USB", "SSB"]:
|
2312
|
-
self.voice_string(self.process_macro(function_key.toolTip()))
|
2275
|
+
self.voice_process.voice_string(self.process_macro(function_key.toolTip()))
|
2276
|
+
# self.voice_string(self.process_macro(function_key.toolTip()))
|
2313
2277
|
return
|
2314
2278
|
if self.cw:
|
2315
2279
|
if self.pref.get("cwtype") == 3 and self.rig_control is not None:
|
@@ -3103,6 +3067,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3103
3067
|
|
3104
3068
|
if self.opon_dialog.NewOperator.text():
|
3105
3069
|
self.current_op = self.opon_dialog.NewOperator.text().upper()
|
3070
|
+
self.voice_process.current_op = self.current_op
|
3106
3071
|
self.opon_dialog.close()
|
3107
3072
|
logger.debug("New Op: %s", self.current_op)
|
3108
3073
|
self.make_op_dir()
|
not1mm/lib/version.py
CHANGED
not1mm/voice_keying.py
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
"""
|
4
|
+
Not1MM Contest logger
|
5
|
+
Email: michael.bridak@gmail.com
|
6
|
+
GPL V3
|
7
|
+
"""
|
8
|
+
|
9
|
+
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines
|
10
|
+
# pylint: disable=logging-fstring-interpolation, line-too-long, no-name-in-module
|
11
|
+
|
12
|
+
import logging
|
13
|
+
from pathlib import Path
|
14
|
+
|
15
|
+
try:
|
16
|
+
import sounddevice as sd
|
17
|
+
except OSError as exception:
|
18
|
+
print(exception)
|
19
|
+
print("portaudio is not installed")
|
20
|
+
sd = None
|
21
|
+
import soundfile as sf
|
22
|
+
|
23
|
+
from PyQt6.QtCore import QObject, pyqtSignal, QThread
|
24
|
+
|
25
|
+
logger = logging.getLogger("cat_interface")
|
26
|
+
|
27
|
+
|
28
|
+
class Voice(QObject):
|
29
|
+
"""Voice class"""
|
30
|
+
|
31
|
+
ptt_on = pyqtSignal()
|
32
|
+
ptt_off = pyqtSignal()
|
33
|
+
data_path = None
|
34
|
+
current_op = None
|
35
|
+
sounddevice = None
|
36
|
+
voicings = []
|
37
|
+
|
38
|
+
def __init__(self) -> None:
|
39
|
+
super().__init__()
|
40
|
+
"""setup interface"""
|
41
|
+
|
42
|
+
def run(self):
|
43
|
+
while True:
|
44
|
+
keyed = False
|
45
|
+
while len(self.voicings):
|
46
|
+
if not keyed:
|
47
|
+
self.ptt_on.emit()
|
48
|
+
keyed = True
|
49
|
+
filename = self.voicings.pop(0)
|
50
|
+
if Path(filename).is_file():
|
51
|
+
logger.debug("Voicing: %s", filename)
|
52
|
+
data, _fs = sf.read(filename, dtype="float32")
|
53
|
+
# self.ptt_on.emit()
|
54
|
+
try:
|
55
|
+
sd.default.device = self.sounddevice
|
56
|
+
sd.default.samplerate = 44100.0
|
57
|
+
sd.play(data, blocking=True)
|
58
|
+
# _status = sd.wait()
|
59
|
+
# https://snyk.io/advisor/python/sounddevice/functions/sounddevice.PortAudioError
|
60
|
+
except sd.PortAudioError as err:
|
61
|
+
logger.warning("%s", f"{err}")
|
62
|
+
if keyed:
|
63
|
+
self.ptt_off.emit()
|
64
|
+
QThread.msleep(100)
|
65
|
+
|
66
|
+
def voice_string(self, the_string: str) -> None:
|
67
|
+
"""
|
68
|
+
voices string using nato phonetics.
|
69
|
+
|
70
|
+
Parameters
|
71
|
+
----------
|
72
|
+
the_string : str
|
73
|
+
String to voicify.
|
74
|
+
|
75
|
+
Returns
|
76
|
+
-------
|
77
|
+
None
|
78
|
+
"""
|
79
|
+
|
80
|
+
logger.debug("Voicing: %s", the_string)
|
81
|
+
if sd is None:
|
82
|
+
logger.warning("Sounddevice/portaudio not installed.")
|
83
|
+
return
|
84
|
+
# fsutils.USER_DATA_PATH
|
85
|
+
# self.current_op
|
86
|
+
op_path = self.data_path / self.current_op
|
87
|
+
if "[" in the_string:
|
88
|
+
sub_string = the_string.strip("[]").lower()
|
89
|
+
filename = f"{str(op_path)}/{sub_string}.wav"
|
90
|
+
if Path(filename).is_file():
|
91
|
+
self.voicings.append(filename)
|
92
|
+
return
|
93
|
+
for letter in the_string.lower():
|
94
|
+
if letter in "abcdefghijklmnopqrstuvwxyz 1234567890":
|
95
|
+
if letter == " ":
|
96
|
+
letter = "space"
|
97
|
+
filename = f"{str(op_path)}/{letter}.wav"
|
98
|
+
if Path(filename).is_file():
|
99
|
+
logger.debug("Voicing: %s", filename)
|
100
|
+
self.voicings.append(filename)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: not1mm
|
3
|
-
Version: 24.
|
3
|
+
Version: 24.5.1
|
4
4
|
Summary: NOT1MM Logger
|
5
5
|
Author-email: Michael Bridak <michael.bridak@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/mbridak/not1mm
|
@@ -180,24 +180,7 @@ I wish to thank those who've contributed to the project.
|
|
180
180
|
|
181
181
|
## Recent Changes
|
182
182
|
|
183
|
-
- [24-
|
184
|
-
- [24-4-27] Cleaned up dark mode code.
|
185
|
-
- [24-4-25-1] Reduce delta time to poll. Reorder poll_radio callback. Remove unused CAT lib from main.
|
186
|
-
- [24-4-25] Limited loop in radio.py, reducing clock cycles used. Moved Log window to the top of the logger.
|
187
|
-
- [24-4-24] Placed CAT control into a thread so disconnecting the radio wouldn't lock up the interface.
|
188
|
-
- [24-4-17] Trap OSError if no sound device. Stop fsutils/appdata from creating useless .not1mm and .username folder structures on Linux platforms.
|
189
|
-
- [24-4-15] checkwindow.py Tighter results. Changed the call selection to use a single click.
|
190
|
-
- [24-4-9-4] Check for portaudio instead of crash boom. Removed empty dockwidget. Tested on Plasma 6.
|
191
|
-
- [24-4-9-3] Ugh. It's not a real day unless you forget to test.
|
192
|
-
- [24-4-9-2] Put back the floatable dock widgets, 'cause Wayland strikes again.
|
193
|
-
- [24-4-9-1] Removed DockWidgetFloatable from the dock widgets since my wee brain can't figure out how to add a dragable window frame to them once they are floating. Added a minimum size for the VFO LCD digits. Defaulted bandmap window to the right.
|
194
|
-
- [24-4-9] Fixed Checkwindow not showing calls from logged contacts.
|
195
|
-
- [24-4-7] Added FT8Watcher class to prep for FT8 support.
|
196
|
-
- [24-4-4-1] Made docking widgets open state persistent.
|
197
|
-
- [24-4-4] Added per-contest echange hint when adding new contest.
|
198
|
-
- [24-4-2] Migrated to PyQt6. I'm sure there are broken things.
|
199
|
-
- [24-4-1-2] Added color text indicators to the Check Partial window. Poached the code from @kyleboyle. Thanks! Fixed the Log, VFO and Check Partial windows to be actual docking widgets. Refocus call field after double clicking on item in the check partial window.
|
200
|
-
- [24-4-1] Removed some un-needed loops and widgets from the check window. Fixed docking to the left side.
|
183
|
+
- [24-5-1] Moved the voice keying into it's own thread.
|
201
184
|
|
202
185
|
See [CHANGELOG.md](CHANGELOG.md) for prior changes.
|
203
186
|
|
@@ -1,11 +1,12 @@
|
|
1
1
|
not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
not1mm/__main__.py,sha256=
|
2
|
+
not1mm/__main__.py,sha256=Y-caHjEPyBoWQwEPcaXGB_dv-OGYcemu4v2N8n7mBes,120135
|
3
3
|
not1mm/bandmap.py,sha256=DyAIgEfV_Ceg_BuBwvV3a2aRHTG3M63ZokoULS0wTdk,32071
|
4
4
|
not1mm/checkwindow.py,sha256=PHQhyN-25ZugbxKKjSxs0wqD80GTHYdIs1IIBjCGVc8,10278
|
5
5
|
not1mm/fsutils.py,sha256=ukHKxKTeNKxKwqRaJjtzRShL4X5Xl0jRBbADyy3Ifp8,1701
|
6
6
|
not1mm/logwindow.py,sha256=MpEVvnuQh9P5Z8kK2MozAaMCNSx1C4isAEYCETI7SjI,44285
|
7
7
|
not1mm/radio.py,sha256=R_RaxdiqcMzIQN2za7qBsStrT5X0AwZcz8xtiCwsYdE,3097
|
8
8
|
not1mm/vfo.py,sha256=7CMI0h9jCEhaonOIvN_7wSiEnsSSxsB1IvOYC9bML6w,11889
|
9
|
+
not1mm/voice_keying.py,sha256=JBesl98FKjW57E84UMFRsa9roUIVOTmA6D1RB-wB6A0,3028
|
9
10
|
not1mm/data/JetBrainsMono-Regular.ttf,sha256=UOHctAKY_PzCGh7zy-6f6egnCcSK0wzmF0csBqO9lDY,203952
|
10
11
|
not1mm/data/MASTER.SCP,sha256=1vQRvEZ865brfmmajp-Lj-hgWejVGI992q8o971bUV8,366478
|
11
12
|
not1mm/data/about.ui,sha256=7TqvtXFFm0Rmcu0bmLupwpO1CsK8MekfZ09_xn6kZrQ,2067
|
@@ -106,7 +107,7 @@ not1mm/lib/plugin_common.py,sha256=AAKBPCXzTWZJb-h08uPNnHVG7bSCg7kwukc211gFivY,8
|
|
106
107
|
not1mm/lib/select_contest.py,sha256=WsptLuwkouIHeocJL3oZ6-eUfEnhpwdc-x7eMZ_TIVM,359
|
107
108
|
not1mm/lib/settings.py,sha256=MWiKXbasaFbzeHTjfzTaTqbCBrIijudP_-0a5jNmUAA,9265
|
108
109
|
not1mm/lib/super_check_partial.py,sha256=p5l3u2ZOCBtlWgbvskC50FpuoaIpR07tfC6zTdRWbh4,2334
|
109
|
-
not1mm/lib/version.py,sha256=
|
110
|
+
not1mm/lib/version.py,sha256=X6y94UOK3VvJjiS_mmZqSRNwFVaWCYBQqbO-fuTep4Q,47
|
110
111
|
not1mm/lib/versiontest.py,sha256=8vDNptuBBunn-1IGkjNaquehqBYUJyjrPSF8Igmd4_Y,1286
|
111
112
|
not1mm/plugins/10_10_fall_cw.py,sha256=fUjfwjuscDjicXIxsO0JHh7xTR9Vu0iPsrOLb896Qak,10873
|
112
113
|
not1mm/plugins/10_10_spring_cw.py,sha256=WNaJP5mBQfaB6SxnFI0Vawt3AKDr94tKVtAK-EVhtUY,10878
|
@@ -140,9 +141,9 @@ not1mm/plugins/naqp_ssb.py,sha256=IWksulcb2_DxlkeW0h3048t8I-u00G_67KBVKkp-TV4,11
|
|
140
141
|
not1mm/plugins/phone_weekly_test.py,sha256=gCX0ESUoiQzDp9puwibt9-dRembNsiuEeBdawCVvjHA,12316
|
141
142
|
not1mm/plugins/stew_perry_topband.py,sha256=DIMI3mGMKokXXb9pPLqdhBI6JVnnIs7ZnAL23nFmshE,10588
|
142
143
|
not1mm/plugins/winter_field_day.py,sha256=4rcfRtobwjHO6BNL3WOTHzBmyyeuX79BNGBG8PfjrI8,10238
|
143
|
-
not1mm-24.
|
144
|
-
not1mm-24.
|
145
|
-
not1mm-24.
|
146
|
-
not1mm-24.
|
147
|
-
not1mm-24.
|
148
|
-
not1mm-24.
|
144
|
+
not1mm-24.5.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
145
|
+
not1mm-24.5.1.dist-info/METADATA,sha256=h9kkaE9SwKUVJZbzLDhtE2JuYrk0c3DeFwU_l_c9JZs,27103
|
146
|
+
not1mm-24.5.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
147
|
+
not1mm-24.5.1.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
|
148
|
+
not1mm-24.5.1.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
|
149
|
+
not1mm-24.5.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|