not1mm 24.4.30__tar.gz → 24.5.9__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.
- {not1mm-24.4.30 → not1mm-24.5.9}/PKG-INFO +9 -21
- {not1mm-24.4.30 → not1mm-24.5.9}/README.md +8 -20
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/__main__.py +40 -67
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/bandmap.py +3 -1
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/checkwindow.py +5 -1
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/new_contest.ui +5 -0
- not1mm-24.5.9/not1mm/lib/playsound.py +296 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/version.py +1 -1
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/logwindow.py +5 -1
- not1mm-24.5.9/not1mm/playsoundtest.py +15 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/10_10_fall_cw.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/10_10_spring_cw.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/10_10_summer_phone.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/10_10_winter_phone.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_10m.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_dx_cw.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_dx_ssb.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_ss_cw.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_ss_phone.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_vhf_jan.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_vhf_jun.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_vhf_sep.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/canada_day.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/cq_160_cw.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/cq_160_ssb.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/cq_wpx_cw.py +39 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/cq_wpx_ssb.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/cq_ww_cw.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/cq_ww_ssb.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/cwt.py +26 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/general_logging.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/iaru_hf.py +2 -0
- not1mm-24.5.9/not1mm/plugins/icwc_mst.py +372 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/jidx_cw.py +31 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/jidx_ph.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/naqp_cw.py +32 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/naqp_ssb.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/stew_perry_topband.py +2 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/radio.py +12 -9
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/vfo.py +5 -1
- not1mm-24.5.9/not1mm/voice_keying.py +103 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm.egg-info/PKG-INFO +9 -21
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm.egg-info/SOURCES.txt +4 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/pyproject.toml +1 -1
- {not1mm-24.4.30 → not1mm-24.5.9}/LICENSE +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/__init__.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/JetBrainsMono-Regular.ttf +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/MASTER.SCP +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/about.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/alpha bravo charlie delta.txt +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/bandmap.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/check.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/checkwindow.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/configuration.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/contests.sql +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/cty.json +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/cwmacros.txt +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/donors.html +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/editcontact.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/editmacro.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/greendot.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/k6gte-not1mm.desktop +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/k6gte.not1mm-128.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/k6gte.not1mm-32.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/k6gte.not1mm-64.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/logwindow.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/logwindowx.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/main.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/not1mm.html +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/opon.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/0.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/1.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/2.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/3.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/4.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/5.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/6.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/7.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/73.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/8.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/9.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/a.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/again.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/b.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/c.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/contest.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/cq.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/d.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/e.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/f.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/g.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/h.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/i.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/j.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/k.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/k6gte.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/l.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/m.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/mynumber.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/n.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/nil.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/o.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/p.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/q.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/r.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/roger.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/s.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/space.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/t.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/thankyou.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/thankyouqrz.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/u.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/v.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/w.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/x.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/y.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/yourcall.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/phonetics/z.wav +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/pickcontest.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/radio_green.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/radio_grey.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/radio_red.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/reddot.png +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/settings.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/ssbmacros.txt +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/data/vfo.ui +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/fsutils.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/__init__.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/about.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/cat_interface.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/cwinterface.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/database.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/edit_contact.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/edit_macro.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/edit_opon.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/edit_station.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/ft8_watcher.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/ham_utility.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/lookup.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/multicast.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/n1mm.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/new_contest.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/plugin_common.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/select_contest.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/settings.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/super_check_partial.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/lib/versiontest.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/__init__.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_field_day.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/arrl_rtty_ru.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/phone_weekly_test.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm/plugins/winter_field_day.py +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm.egg-info/dependency_links.txt +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm.egg-info/entry_points.txt +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm.egg-info/requires.txt +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/not1mm.egg-info/top_level.txt +0 -0
- {not1mm-24.4.30 → not1mm-24.5.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: not1mm
|
3
|
-
Version: 24.
|
3
|
+
Version: 24.5.9
|
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
|
@@ -169,6 +169,7 @@ I wish to thank those who've contributed to the project.
|
|
169
169
|
- CQ World Wide SSB
|
170
170
|
- CWOps CWT
|
171
171
|
- IARU HF
|
172
|
+
- ICWC MST
|
172
173
|
- Japan International DX CW
|
173
174
|
- Japan International DX SSB
|
174
175
|
- NAQP CW
|
@@ -180,24 +181,8 @@ I wish to thank those who've contributed to the project.
|
|
180
181
|
|
181
182
|
## Recent Changes
|
182
183
|
|
183
|
-
- [24-
|
184
|
-
- [24-
|
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.
|
184
|
+
- [24-5-9] Add ICWC MST.
|
185
|
+
- [24-5-1] Moved the voice keying into it's own thread.
|
201
186
|
|
202
187
|
See [CHANGELOG.md](CHANGELOG.md) for prior changes.
|
203
188
|
|
@@ -229,8 +214,11 @@ noted the minimum steps needed to install not1mm.
|
|
229
214
|
```bash
|
230
215
|
sudo apt update
|
231
216
|
sudo apt upgrade
|
232
|
-
sudo apt install -y
|
233
|
-
pip install -U
|
217
|
+
sudo apt install -y python3-pip python3-numpy libxcb-cursor0 libportaudio2
|
218
|
+
python3 -m pip install -U pip
|
219
|
+
# Logout and back in
|
220
|
+
pip3 install PyQt6
|
221
|
+
pip3 install not1mm
|
234
222
|
```
|
235
223
|
|
236
224
|
#### Ubuntu 23.04
|
@@ -137,6 +137,7 @@ I wish to thank those who've contributed to the project.
|
|
137
137
|
- CQ World Wide SSB
|
138
138
|
- CWOps CWT
|
139
139
|
- IARU HF
|
140
|
+
- ICWC MST
|
140
141
|
- Japan International DX CW
|
141
142
|
- Japan International DX SSB
|
142
143
|
- NAQP CW
|
@@ -148,24 +149,8 @@ I wish to thank those who've contributed to the project.
|
|
148
149
|
|
149
150
|
## Recent Changes
|
150
151
|
|
151
|
-
- [24-
|
152
|
-
- [24-
|
153
|
-
- [24-4-25-1] Reduce delta time to poll. Reorder poll_radio callback. Remove unused CAT lib from main.
|
154
|
-
- [24-4-25] Limited loop in radio.py, reducing clock cycles used. Moved Log window to the top of the logger.
|
155
|
-
- [24-4-24] Placed CAT control into a thread so disconnecting the radio wouldn't lock up the interface.
|
156
|
-
- [24-4-17] Trap OSError if no sound device. Stop fsutils/appdata from creating useless .not1mm and .username folder structures on Linux platforms.
|
157
|
-
- [24-4-15] checkwindow.py Tighter results. Changed the call selection to use a single click.
|
158
|
-
- [24-4-9-4] Check for portaudio instead of crash boom. Removed empty dockwidget. Tested on Plasma 6.
|
159
|
-
- [24-4-9-3] Ugh. It's not a real day unless you forget to test.
|
160
|
-
- [24-4-9-2] Put back the floatable dock widgets, 'cause Wayland strikes again.
|
161
|
-
- [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.
|
162
|
-
- [24-4-9] Fixed Checkwindow not showing calls from logged contacts.
|
163
|
-
- [24-4-7] Added FT8Watcher class to prep for FT8 support.
|
164
|
-
- [24-4-4-1] Made docking widgets open state persistent.
|
165
|
-
- [24-4-4] Added per-contest echange hint when adding new contest.
|
166
|
-
- [24-4-2] Migrated to PyQt6. I'm sure there are broken things.
|
167
|
-
- [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.
|
168
|
-
- [24-4-1] Removed some un-needed loops and widgets from the check window. Fixed docking to the left side.
|
152
|
+
- [24-5-9] Add ICWC MST.
|
153
|
+
- [24-5-1] Moved the voice keying into it's own thread.
|
169
154
|
|
170
155
|
See [CHANGELOG.md](CHANGELOG.md) for prior changes.
|
171
156
|
|
@@ -197,8 +182,11 @@ noted the minimum steps needed to install not1mm.
|
|
197
182
|
```bash
|
198
183
|
sudo apt update
|
199
184
|
sudo apt upgrade
|
200
|
-
sudo apt install -y
|
201
|
-
pip install -U
|
185
|
+
sudo apt install -y python3-pip python3-numpy libxcb-cursor0 libportaudio2
|
186
|
+
python3 -m pip install -U pip
|
187
|
+
# Logout and back in
|
188
|
+
pip3 install PyQt6
|
189
|
+
pip3 install not1mm
|
202
190
|
```
|
203
191
|
|
204
192
|
#### Ubuntu 23.04
|
@@ -1,6 +1,9 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
|
-
|
3
|
+
not1mm Contest logger
|
4
|
+
Email: michael.bridak@gmail.com
|
5
|
+
GPL V3
|
6
|
+
Purpose: Provides main logging window and a crap ton more.
|
4
7
|
"""
|
5
8
|
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines, no-name-in-module
|
6
9
|
# pylint: disable=logging-fstring-interpolation, logging-not-lazy, line-too-long, bare-except
|
@@ -35,7 +38,6 @@ except OSError as exception:
|
|
35
38
|
print(exception)
|
36
39
|
print("portaudio is not installed")
|
37
40
|
sd = None
|
38
|
-
import soundfile as sf
|
39
41
|
from PyQt6 import QtCore, QtGui, QtWidgets, uic
|
40
42
|
from PyQt6.QtCore import QDir, Qt, QThread
|
41
43
|
from PyQt6.QtGui import QFontDatabase, QColorConstants, QPalette, QColor
|
@@ -75,6 +77,7 @@ from not1mm.checkwindow import CheckWindow
|
|
75
77
|
from not1mm.bandmap import BandMapWindow
|
76
78
|
from not1mm.vfo import VfoWindow
|
77
79
|
from not1mm.radio import Radio
|
80
|
+
from not1mm.voice_keying import Voice
|
78
81
|
|
79
82
|
poll_time = datetime.datetime.now()
|
80
83
|
|
@@ -188,6 +191,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
188
191
|
current_palette = None
|
189
192
|
|
190
193
|
radio_thread = QThread()
|
194
|
+
voice_thread = QThread()
|
191
195
|
|
192
196
|
rig_control = None
|
193
197
|
log_window = None
|
@@ -479,6 +483,18 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
479
483
|
with open(fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8") as c_file:
|
480
484
|
self.ctyfile = loads(c_file.read())
|
481
485
|
self.readpreferences()
|
486
|
+
|
487
|
+
self.voice_process = Voice()
|
488
|
+
self.voice_process.moveToThread(self.voice_thread)
|
489
|
+
self.voice_thread.started.connect(self.voice_process.run)
|
490
|
+
self.voice_thread.finished.connect(self.voice_process.deleteLater)
|
491
|
+
self.voice_process.ptt_on.connect(self.ptt_on)
|
492
|
+
self.voice_process.ptt_off.connect(self.ptt_off)
|
493
|
+
self.voice_process.current_op = self.current_op
|
494
|
+
self.voice_process.data_path = fsutils.USER_DATA_PATH
|
495
|
+
self.voice_process.sounddevice = self.pref.get("sounddevice", "default")
|
496
|
+
self.voice_thread.start()
|
497
|
+
|
482
498
|
self.dbname = fsutils.USER_DATA_PATH / self.pref.get(
|
483
499
|
"current_database", "ham.db"
|
484
500
|
)
|
@@ -492,6 +508,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
492
508
|
self.station = {}
|
493
509
|
self.contact = self.database.empty_contact
|
494
510
|
self.current_op = self.station.get("Call", "")
|
511
|
+
self.voice_process.current_op = self.current_op
|
495
512
|
self.make_op_dir()
|
496
513
|
self.read_cw_macros()
|
497
514
|
self.clearinputs()
|
@@ -922,6 +939,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
922
939
|
if self.station is None:
|
923
940
|
self.station = {}
|
924
941
|
self.current_op = self.station.get("Call", "")
|
942
|
+
self.voice_process.current_op = self.current_op
|
925
943
|
self.make_op_dir()
|
926
944
|
cmd = {}
|
927
945
|
cmd["cmd"] = "NEWDB"
|
@@ -958,6 +976,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
958
976
|
if self.station.get("Call", "") == "":
|
959
977
|
self.edit_station_settings()
|
960
978
|
self.current_op = self.station.get("Call", "")
|
979
|
+
self.voice_process.current_op = self.current_op
|
961
980
|
self.make_op_dir()
|
962
981
|
cmd = {}
|
963
982
|
cmd["cmd"] = "NEWDB"
|
@@ -1231,6 +1250,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1231
1250
|
else:
|
1232
1251
|
self.cw_speed.hide()
|
1233
1252
|
|
1253
|
+
self.clearinputs()
|
1234
1254
|
cmd = {}
|
1235
1255
|
cmd["cmd"] = "NEWDB"
|
1236
1256
|
cmd["station"] = platform.node()
|
@@ -1832,6 +1852,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1832
1852
|
self.contact = self.database.empty_contact
|
1833
1853
|
self.heading_distance.setText("")
|
1834
1854
|
self.dx_entity.setText("")
|
1855
|
+
|
1835
1856
|
if self.contest:
|
1836
1857
|
mults = self.contest.show_mults(self)
|
1837
1858
|
qsos = self.contest.show_qso(self)
|
@@ -1840,13 +1861,16 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1840
1861
|
score = self.contest.calc_score(self)
|
1841
1862
|
self.score.setText(str(score))
|
1842
1863
|
self.contest.reset_label(self)
|
1864
|
+
if self.contest.name != "ICWC Medium Speed Test":
|
1865
|
+
if self.current_mode == "CW":
|
1866
|
+
self.sent.setText("599")
|
1867
|
+
self.receive.setText("599")
|
1868
|
+
else:
|
1869
|
+
self.sent.setText("59")
|
1870
|
+
self.receive.setText("59")
|
1871
|
+
else:
|
1872
|
+
self.sent.setText("")
|
1843
1873
|
self.callsign.clear()
|
1844
|
-
if self.current_mode == "CW":
|
1845
|
-
self.sent.setText("599")
|
1846
|
-
self.receive.setText("599")
|
1847
|
-
else:
|
1848
|
-
self.sent.setText("59")
|
1849
|
-
self.receive.setText("59")
|
1850
1874
|
self.other_1.clear()
|
1851
1875
|
self.other_2.clear()
|
1852
1876
|
self.callsign.setFocus()
|
@@ -2118,6 +2142,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2118
2142
|
self.settings_dialog.close()
|
2119
2143
|
if self.current_op == "":
|
2120
2144
|
self.current_op = self.station.get("Call", "")
|
2145
|
+
self.voice_process.current_op = self.current_op
|
2121
2146
|
self.make_op_dir()
|
2122
2147
|
contest_count = self.database.fetch_all_contests()
|
2123
2148
|
if len(contest_count) == 0:
|
@@ -2199,61 +2224,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2199
2224
|
)
|
2200
2225
|
return macro
|
2201
2226
|
|
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
2227
|
def ptt_on(self) -> None:
|
2258
2228
|
"""
|
2259
2229
|
Turn on ptt for rig.
|
@@ -2285,6 +2255,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2285
2255
|
-------
|
2286
2256
|
None
|
2287
2257
|
"""
|
2258
|
+
|
2288
2259
|
logger.debug("PTT Off")
|
2289
2260
|
if self.rig_control:
|
2290
2261
|
self.leftdot.setPixmap(self.reddot)
|
@@ -2309,7 +2280,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2309
2280
|
if self.n1mm:
|
2310
2281
|
self.n1mm.radio_info["FunctionKeyCaption"] = function_key.text()
|
2311
2282
|
if self.radio_state.get("mode") in ["LSB", "USB", "SSB"]:
|
2312
|
-
self.voice_string(self.process_macro(function_key.toolTip()))
|
2283
|
+
self.voice_process.voice_string(self.process_macro(function_key.toolTip()))
|
2284
|
+
# self.voice_string(self.process_macro(function_key.toolTip()))
|
2313
2285
|
return
|
2314
2286
|
if self.cw:
|
2315
2287
|
if self.pref.get("cwtype") == 3 and self.rig_control is not None:
|
@@ -3046,15 +3018,16 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3046
3018
|
if mode == "CW":
|
3047
3019
|
if self.current_mode != "CW":
|
3048
3020
|
self.current_mode = "CW"
|
3049
|
-
# self.mode.setText("CW")
|
3050
3021
|
self.sent.setText("599")
|
3051
3022
|
self.receive.setText("599")
|
3052
3023
|
self.read_cw_macros()
|
3024
|
+
if self.contest:
|
3025
|
+
if self.contest.name == "ICWC Medium Speed Test":
|
3026
|
+
self.contest.prefill(self)
|
3053
3027
|
return
|
3054
3028
|
if mode == "SSB":
|
3055
3029
|
if self.current_mode != "SSB":
|
3056
3030
|
self.current_mode = "SSB"
|
3057
|
-
# self.mode.setText("SSB")
|
3058
3031
|
self.sent.setText("59")
|
3059
3032
|
self.receive.setText("59")
|
3060
3033
|
self.read_cw_macros()
|
@@ -3062,7 +3035,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3062
3035
|
if mode == "RTTY":
|
3063
3036
|
if self.current_mode != "RTTY":
|
3064
3037
|
self.current_mode = "RTTY"
|
3065
|
-
# self.mode.setText("RTTY")
|
3066
3038
|
self.sent.setText("59")
|
3067
3039
|
self.receive.setText("59")
|
3068
3040
|
|
@@ -3103,6 +3075,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3103
3075
|
|
3104
3076
|
if self.opon_dialog.NewOperator.text():
|
3105
3077
|
self.current_op = self.opon_dialog.NewOperator.text().upper()
|
3078
|
+
self.voice_process.current_op = self.current_op
|
3106
3079
|
self.opon_dialog.close()
|
3107
3080
|
logger.debug("New Op: %s", self.current_op)
|
3108
3081
|
self.make_op_dir()
|
@@ -1,8 +1,10 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
|
-
|
3
|
+
not1mm Contest logger
|
4
4
|
Email: michael.bridak@gmail.com
|
5
5
|
GPL V3
|
6
|
+
Class: BandMapWindow
|
7
|
+
Purpose: Onscreen widget to show realtime spots from an AR cluster.
|
6
8
|
"""
|
7
9
|
|
8
10
|
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines
|
@@ -1,6 +1,10 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
|
-
|
3
|
+
not1mm Contest logger
|
4
|
+
Email: michael.bridak@gmail.com
|
5
|
+
GPL V3
|
6
|
+
Class: CheckWindow
|
7
|
+
Purpose: Onscreen widget to show possible matches to callsigns entered in the main window.
|
4
8
|
"""
|
5
9
|
# pylint: disable=no-name-in-module, unused-import, no-member, invalid-name, c-extension-no-member
|
6
10
|
# pylint: disable=logging-fstring-interpolation, line-too-long
|
@@ -0,0 +1,296 @@
|
|
1
|
+
import logging
|
2
|
+
from platform import system
|
3
|
+
|
4
|
+
logger = logging.getLogger(__name__)
|
5
|
+
|
6
|
+
|
7
|
+
class PlaysoundException(Exception):
|
8
|
+
pass
|
9
|
+
|
10
|
+
|
11
|
+
def _canonicalizePath(path):
|
12
|
+
"""
|
13
|
+
Support passing in a pathlib.Path-like object by converting to str.
|
14
|
+
"""
|
15
|
+
import sys
|
16
|
+
|
17
|
+
if sys.version_info[0] >= 3:
|
18
|
+
return str(path)
|
19
|
+
else:
|
20
|
+
# On earlier Python versions, str is a byte string, so attempting to
|
21
|
+
# convert a unicode string to str will fail. Leave it alone in this case.
|
22
|
+
return path
|
23
|
+
|
24
|
+
|
25
|
+
def _playsoundWin(sound, block=True):
|
26
|
+
"""
|
27
|
+
Utilizes windll.winmm. Tested and known to work with MP3 and WAVE on
|
28
|
+
Windows 7 with Python 2.7. Probably works with more file formats.
|
29
|
+
Probably works on Windows XP thru Windows 10. Probably works with all
|
30
|
+
versions of Python.
|
31
|
+
|
32
|
+
Inspired by (but not copied from) Michael Gundlach <gundlach@gmail.com>'s mp3play:
|
33
|
+
https://github.com/michaelgundlach/mp3play
|
34
|
+
|
35
|
+
I never would have tried using windll.winmm without seeing his code.
|
36
|
+
"""
|
37
|
+
sound = '"' + _canonicalizePath(sound) + '"'
|
38
|
+
|
39
|
+
from ctypes import create_unicode_buffer, windll, wintypes
|
40
|
+
|
41
|
+
windll.winmm.mciSendStringW.argtypes = [
|
42
|
+
wintypes.LPCWSTR,
|
43
|
+
wintypes.LPWSTR,
|
44
|
+
wintypes.UINT,
|
45
|
+
wintypes.HANDLE,
|
46
|
+
]
|
47
|
+
windll.winmm.mciGetErrorStringW.argtypes = [
|
48
|
+
wintypes.DWORD,
|
49
|
+
wintypes.LPWSTR,
|
50
|
+
wintypes.UINT,
|
51
|
+
]
|
52
|
+
|
53
|
+
def winCommand(*command):
|
54
|
+
bufLen = 600
|
55
|
+
buf = create_unicode_buffer(bufLen)
|
56
|
+
command = " ".join(command)
|
57
|
+
errorCode = int(
|
58
|
+
windll.winmm.mciSendStringW(command, buf, bufLen - 1, 0)
|
59
|
+
) # use widestring version of the function
|
60
|
+
if errorCode:
|
61
|
+
errorBuffer = create_unicode_buffer(bufLen)
|
62
|
+
windll.winmm.mciGetErrorStringW(
|
63
|
+
errorCode, errorBuffer, bufLen - 1
|
64
|
+
) # use widestring version of the function
|
65
|
+
exceptionMessage = (
|
66
|
+
"\n Error " + str(errorCode) + " for command:"
|
67
|
+
"\n " + command + "\n " + errorBuffer.value
|
68
|
+
)
|
69
|
+
logger.error(exceptionMessage)
|
70
|
+
raise PlaysoundException(exceptionMessage)
|
71
|
+
return buf.value
|
72
|
+
|
73
|
+
try:
|
74
|
+
logger.debug("Starting")
|
75
|
+
winCommand("open {}".format(sound))
|
76
|
+
winCommand("play {}{}".format(sound, " wait" if block else ""))
|
77
|
+
logger.debug("Returning")
|
78
|
+
finally:
|
79
|
+
try:
|
80
|
+
winCommand("close {}".format(sound))
|
81
|
+
except PlaysoundException:
|
82
|
+
logger.warning("Failed to close the file: {}".format(sound))
|
83
|
+
# If it fails, there's nothing more that can be done...
|
84
|
+
pass
|
85
|
+
|
86
|
+
|
87
|
+
def _handlePathOSX(sound):
|
88
|
+
sound = _canonicalizePath(sound)
|
89
|
+
|
90
|
+
if "://" not in sound:
|
91
|
+
if not sound.startswith("/"):
|
92
|
+
from os import getcwd
|
93
|
+
|
94
|
+
sound = getcwd() + "/" + sound
|
95
|
+
sound = "file://" + sound
|
96
|
+
|
97
|
+
try:
|
98
|
+
# Don't double-encode it.
|
99
|
+
sound.encode("ascii")
|
100
|
+
return sound.replace(" ", "%20")
|
101
|
+
except UnicodeEncodeError:
|
102
|
+
try:
|
103
|
+
from urllib.parse import quote # Try the Python 3 import first...
|
104
|
+
except ImportError:
|
105
|
+
from urllib import (
|
106
|
+
quote,
|
107
|
+
) # Try using the Python 2 import before giving up entirely...
|
108
|
+
|
109
|
+
parts = sound.split("://", 1)
|
110
|
+
return parts[0] + "://" + quote(parts[1].encode("utf-8")).replace(" ", "%20")
|
111
|
+
|
112
|
+
|
113
|
+
def _playsoundOSX(sound, block=True):
|
114
|
+
"""
|
115
|
+
Utilizes AppKit.NSSound. Tested and known to work with MP3 and WAVE on
|
116
|
+
OS X 10.11 with Python 2.7. Probably works with anything QuickTime supports.
|
117
|
+
Probably works on OS X 10.5 and newer. Probably works with all versions of
|
118
|
+
Python.
|
119
|
+
|
120
|
+
Inspired by (but not copied from) Aaron's Stack Overflow answer here:
|
121
|
+
http://stackoverflow.com/a/34568298/901641
|
122
|
+
|
123
|
+
I never would have tried using AppKit.NSSound without seeing his code.
|
124
|
+
"""
|
125
|
+
try:
|
126
|
+
from AppKit import NSSound
|
127
|
+
except ImportError:
|
128
|
+
logger.warning(
|
129
|
+
"playsound could not find a copy of AppKit - falling back to using macOS's system copy."
|
130
|
+
)
|
131
|
+
sys.path.append(
|
132
|
+
"/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC"
|
133
|
+
)
|
134
|
+
from AppKit import NSSound
|
135
|
+
|
136
|
+
from Foundation import NSURL
|
137
|
+
from time import sleep
|
138
|
+
|
139
|
+
sound = _handlePathOSX(sound)
|
140
|
+
url = NSURL.URLWithString_(sound)
|
141
|
+
if not url:
|
142
|
+
raise PlaysoundException("Cannot find a sound with filename: " + sound)
|
143
|
+
|
144
|
+
for i in range(5):
|
145
|
+
nssound = NSSound.alloc().initWithContentsOfURL_byReference_(url, True)
|
146
|
+
if nssound:
|
147
|
+
break
|
148
|
+
else:
|
149
|
+
logger.debug("Failed to load sound, although url was good... " + sound)
|
150
|
+
else:
|
151
|
+
raise PlaysoundException(
|
152
|
+
"Could not load sound with filename, although URL was good... " + sound
|
153
|
+
)
|
154
|
+
nssound.play()
|
155
|
+
|
156
|
+
if block:
|
157
|
+
sleep(nssound.duration())
|
158
|
+
|
159
|
+
|
160
|
+
def _playsoundNix(sound, block=True):
|
161
|
+
"""Play a sound using GStreamer.
|
162
|
+
|
163
|
+
Inspired by this:
|
164
|
+
https://gstreamer.freedesktop.org/documentation/tutorials/playback/playbin-usage.html
|
165
|
+
"""
|
166
|
+
sound = _canonicalizePath(sound)
|
167
|
+
|
168
|
+
# pathname2url escapes non-URL-safe characters
|
169
|
+
from os.path import abspath, exists
|
170
|
+
|
171
|
+
try:
|
172
|
+
from urllib.request import pathname2url
|
173
|
+
except ImportError:
|
174
|
+
# python 2
|
175
|
+
from urllib import pathname2url
|
176
|
+
|
177
|
+
import gi
|
178
|
+
|
179
|
+
gi.require_version("Gst", "1.0")
|
180
|
+
from gi.repository import Gst
|
181
|
+
|
182
|
+
Gst.init(None)
|
183
|
+
|
184
|
+
playbin = Gst.ElementFactory.make("playbin", "playbin")
|
185
|
+
if sound.startswith(("http://", "https://")):
|
186
|
+
playbin.props.uri = sound
|
187
|
+
else:
|
188
|
+
path = abspath(sound)
|
189
|
+
if not exists(path):
|
190
|
+
raise PlaysoundException("File not found: {}".format(path))
|
191
|
+
playbin.props.uri = "file://" + pathname2url(path)
|
192
|
+
|
193
|
+
set_result = playbin.set_state(Gst.State.PLAYING)
|
194
|
+
if set_result != Gst.StateChangeReturn.ASYNC:
|
195
|
+
raise PlaysoundException("playbin.set_state returned " + repr(set_result))
|
196
|
+
|
197
|
+
# FIXME: use some other bus method than poll() with block=False
|
198
|
+
# https://lazka.github.io/pgi-docs/#Gst-1.0/classes/Bus.html
|
199
|
+
logger.debug("Starting play")
|
200
|
+
if block:
|
201
|
+
bus = playbin.get_bus()
|
202
|
+
try:
|
203
|
+
bus.poll(Gst.MessageType.EOS, Gst.CLOCK_TIME_NONE)
|
204
|
+
finally:
|
205
|
+
playbin.set_state(Gst.State.NULL)
|
206
|
+
|
207
|
+
logger.debug("Finishing play")
|
208
|
+
|
209
|
+
|
210
|
+
def _playsoundAnotherPython(otherPython, sound, block=True, macOS=False):
|
211
|
+
"""
|
212
|
+
Mostly written so that when this is run on python3 on macOS, it can invoke
|
213
|
+
python2 on macOS... but maybe this idea could be useful on linux, too.
|
214
|
+
"""
|
215
|
+
from inspect import getsourcefile
|
216
|
+
from os.path import abspath, exists
|
217
|
+
from subprocess import check_call
|
218
|
+
from threading import Thread
|
219
|
+
|
220
|
+
sound = _canonicalizePath(sound)
|
221
|
+
|
222
|
+
class PropogatingThread(Thread):
|
223
|
+
def run(self):
|
224
|
+
self.exc = None
|
225
|
+
try:
|
226
|
+
self.ret = self._target(*self._args, **self._kwargs)
|
227
|
+
except BaseException as e:
|
228
|
+
self.exc = e
|
229
|
+
|
230
|
+
def join(self, timeout=None):
|
231
|
+
super().join(timeout)
|
232
|
+
if self.exc:
|
233
|
+
raise self.exc
|
234
|
+
return self.ret
|
235
|
+
|
236
|
+
# Check if the file exists...
|
237
|
+
if not exists(abspath(sound)):
|
238
|
+
raise PlaysoundException("Cannot find a sound with filename: " + sound)
|
239
|
+
|
240
|
+
playsoundPath = abspath(getsourcefile(lambda: 0))
|
241
|
+
t = PropogatingThread(
|
242
|
+
target=lambda: check_call(
|
243
|
+
[otherPython, playsoundPath, _handlePathOSX(sound) if macOS else sound]
|
244
|
+
)
|
245
|
+
)
|
246
|
+
t.start()
|
247
|
+
if block:
|
248
|
+
t.join()
|
249
|
+
|
250
|
+
|
251
|
+
system = system()
|
252
|
+
|
253
|
+
if system == "Windows":
|
254
|
+
playsound = _playsoundWin
|
255
|
+
elif system == "Darwin":
|
256
|
+
playsound = _playsoundOSX
|
257
|
+
import sys
|
258
|
+
|
259
|
+
if sys.version_info[0] > 2:
|
260
|
+
try:
|
261
|
+
from AppKit import NSSound
|
262
|
+
except ImportError:
|
263
|
+
logger.warning(
|
264
|
+
"playsound is relying on a python 2 subprocess. Please use `pip3 install PyObjC` if you want playsound to run more efficiently."
|
265
|
+
)
|
266
|
+
playsound = lambda sound, block=True: _playsoundAnotherPython(
|
267
|
+
"/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python",
|
268
|
+
sound,
|
269
|
+
block,
|
270
|
+
macOS=True,
|
271
|
+
)
|
272
|
+
else:
|
273
|
+
playsound = _playsoundNix
|
274
|
+
if (
|
275
|
+
__name__ != "__main__"
|
276
|
+
): # Ensure we don't infinitely recurse trying to get another python instance.
|
277
|
+
try:
|
278
|
+
import gi
|
279
|
+
|
280
|
+
gi.require_version("Gst", "1.0")
|
281
|
+
from gi.repository import Gst
|
282
|
+
except:
|
283
|
+
logger.warning(
|
284
|
+
"playsound is relying on another python subprocess. Please use `pip install pygobject` if you want playsound to run more efficiently."
|
285
|
+
)
|
286
|
+
playsound = lambda sound, block=True: _playsoundAnotherPython(
|
287
|
+
"/usr/bin/python3", sound, block, macOS=False
|
288
|
+
)
|
289
|
+
|
290
|
+
del system
|
291
|
+
|
292
|
+
if __name__ == "__main__":
|
293
|
+
# block is always True if you choose to run this from the command line.
|
294
|
+
from sys import argv
|
295
|
+
|
296
|
+
playsound(argv[1])
|