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 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
@@ -1,3 +1,3 @@
1
1
  """It's the version"""
2
2
 
3
- __version__ = "24.4.30"
3
+ __version__ = "24.5.1"
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.4.30
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-4-30] Allowsending CW via rigctld if your radio supports it.
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=B2BSFagNTFJ6jWChOEj41TAtsWZ4rXo59sDiKp4rie0,121264
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=zA20U-5Ca6bsgBYAJw2MX5JgzAAvPdJ2B59giQAav24,48
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.4.30.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
144
- not1mm-24.4.30.dist-info/METADATA,sha256=KBVE9BJ01q8RlE8BBesdoAtBiMt5m0z_tnkQnNGyZAY,28897
145
- not1mm-24.4.30.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
146
- not1mm-24.4.30.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
147
- not1mm-24.4.30.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
148
- not1mm-24.4.30.dist-info/RECORD,,
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,,