Skip to content

Commit 0be9eb0

Browse files
committed
- version 0.9.3.11.21
- adding TTS volume properly - adding confirmation message to TTS when config file saved
1 parent f9dcfb9 commit 0be9eb0

File tree

11 files changed

+149
-59
lines changed

11 files changed

+149
-59
lines changed

docs/tts.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ <h2 id="table-of-contents">Table of Contents <span style="padding-left: 10px;"><
7070
<li><a href="#for-multi-language-content">For Multi-Language Content</a></li>
7171
</ul></li>
7272
<li><a href="#configuration-philosophy">Configuration Philosophy</a></li>
73+
<li><a href="#configuration-options">Configuration Options</a>
74+
<ul>
75+
<li><a href="#volume">Volume</a></li>
76+
</ul></li>
7377
<li><a href="#current-status-&amp;-roadmap">Current Status &amp; Roadmap</a></li>
7478
</ul>
7579
<!-- vim-markdown-toc -->
@@ -153,6 +157,15 @@ <h2 id="configuration-philosophy">Configuration Philosophy <span style="padding-
153157
<li>Maintains your preferred speech rate and volume</li>
154158
<li>Platform-native experience throughout</li>
155159
</ul>
160+
<h2 id="configuration-options">Configuration Options <span style="padding-left: 10px;"><sup style="font-size: 50%"><a href="#" title="Go to top of the page">Top</a></sup></span></h2>
161+
<h3 id="volume">Volume</h3>
162+
<p>The TTS volume can be adjusted independently of audio playback volume:</p>
163+
<ul>
164+
<li><strong>Linux</strong>: Uses spd-say with volume range from <strong>-100 to +100</strong> (relative to base volume)</li>
165+
<li><strong>Windows</strong>: Uses SAPI with volume range from <strong>0 to 100</strong> (absolute)</li>
166+
<li><strong>macOS</strong>: Volume control is not supported - uses system speech volume</li>
167+
</ul>
168+
<p>This option can be found in the configuration menu under <strong>Config → TTS → Volume</strong>.</p>
156169
<h2 id="current-status-roadmap">Current Status &amp; Roadmap <span style="padding-left: 10px;"><sup style="font-size: 50%"><a href="#" title="Go to top of the page">Top</a></sup></span></h2>
157170
<p>This TTS implementation is actively developed. Planned enhancements include:</p>
158171
<ul>

docs/tts.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
* [For English-Dominant Users](#for-english-dominant-users)
2727
* [For Multi-Language Content](#for-multi-language-content)
2828
* [Configuration Philosophy](#configuration-philosophy)
29+
* [Configuration Options](#configuration-options)
30+
* [Volume](#volume)
2931
* [Current Status & Roadmap](#current-status-&-roadmap)
3032

3133
<!-- vim-markdown-toc -->
@@ -120,6 +122,18 @@ The system is optimized for English while respecting platform conventions:
120122
- Maintains your preferred speech rate and volume
121123
- Platform-native experience throughout
122124

125+
## Configuration Options
126+
127+
### Volume
128+
129+
The TTS volume can be adjusted independently of audio playback volume:
130+
131+
- **Linux**: Uses `spd-say` with volume range from **-100 to +100** (relative to base volume)
132+
- **Windows**: Uses SAPI with volume range from **0 to 100** (absolute)
133+
- **macOS**: Volume control is not supported - uses system speech volume
134+
135+
This option can be found in the configuration menu under **Config → TTS → Volume**.
136+
123137
## Current Status & Roadmap
124138

125139
This TTS implementation is actively developed. Planned enhancements include:
@@ -128,4 +142,3 @@ This TTS implementation is actively developed. Planned enhancements include:
128142
- Detailed interface element descriptions
129143
- Enhanced context-aware speech patterns
130144
- Extended configuration options
131-

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "pyradio"
3-
version = "0.9.3.11.20"
3+
version = "0.9.3.11.21"
44
authors = [
55
{ name="Ben Dowling", email="[email protected]" },
66
{ name="Spiros Georgaras", email="[email protected]" },

pyradio/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
" pyradio -- Console radio player. "
33

4-
version_info = (0, 9, 3, 11, 20)
4+
version_info = (0, 9, 3, 11, 21)
55

66
# Set it to True if new stations have been
77
# added to the package's stations.csv

pyradio/config

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ remove_station_icons = True
133133
# Default value: False
134134
enable_tts = False
135135

136+
# TTS Volume
137+
# This is the volume to be used by the TTS engine
138+
# It applies to Linux and Windows only
139+
#
140+
# Default value: 50
141+
tts_volume = 50
142+
136143
# Titles logging
137144
# If this option is True, PyRadio will start logging song titles to a log file
138145
# at startup, provided that the station playing does provide title data.

pyradio/config.py

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,7 +1361,8 @@ def _init_vars(self):
13611361
self.opts['use_station_icon'] = [' Use station icon: ', True]
13621362
self.opts['remove_station_icons'] = [' Remove cached icons: ', True]
13631363
self.opts['tts_title'] = ['TTS', '']
1364-
self.opts['enable_tts'] = [' Enable TTS: ', False]
1364+
self.opts['enable_tts'] = ['Enable TTS: ', False]
1365+
self.opts['tts_volume'] = [' Volume: ', '50']
13651366
self.opts['clock_title'] = ['Clock', '']
13661367
self.opts['enable_clock'] = ['Display on startup: ', False]
13671368
self.opts['time_format'] = ['Time format: ', '1']
@@ -1712,6 +1713,16 @@ def enable_tts(self, val):
17121713
self.opts['enable_tts'][1] = val
17131714
self.opts['dirty_config'][1] = True
17141715

1716+
@property
1717+
def tts_volume(self):
1718+
''' connection timeout as string '''
1719+
return self.opts['tts_volume'][1]
1720+
1721+
@tts_volume.setter
1722+
def tts_volume(self, val):
1723+
self.opts['tts_volume'][1] = val
1724+
self.opts['dirty_config'][1] = True
1725+
17151726
@property
17161727
def player(self):
17171728
return self.opts['player'][1]
@@ -2561,6 +2572,8 @@ def _read_config(self, distro_config=False):
25612572
self.opts['enable_tts'][1] = False
25622573
else:
25632574
self.opts['enable_tts'][1] = True
2575+
elif sp[0] == 'tts_volume':
2576+
self.opts['tts_volume'][1] = sp[1]
25642577
elif sp[0] == 'confirm_station_deletion':
25652578
if sp[1].lower() == 'false':
25662579
self.opts['confirm_station_deletion'][1] = False
@@ -3138,31 +3151,6 @@ def save_config(self, from_command_line=False):
31383151
with open(self.config_file, 'w', encoding='utf-8') as cfgfile:
31393152
if out:
31403153
cfgfile.write('\n'.join(out) + '\n')
3141-
# cfgfile.write(txt.format(
3142-
# self.opts['player'][1],
3143-
# self.opts['open_last_playlist'][1],
3144-
# self.opts['default_playlist'][1],
3145-
# self.opts['default_station'][1],
3146-
# self.opts['default_encoding'][1],
3147-
# self.opts['enable_mouse'][1],
3148-
# self.opts['enable_notifications'][1],
3149-
# self.opts['use_station_icon'][1],
3150-
# rec_dir,
3151-
# self.opts['connection_timeout'][1],
3152-
# self.opts['force_http'][1],
3153-
# theme,
3154-
# trnsp,
3155-
# self.opts['force_transparency'][1],
3156-
# calcf,
3157-
# self.opts['confirm_station_deletion'][1],
3158-
# self.opts['confirm_playlist_reload'][1],
3159-
# self.opts['auto_save_playlist'][1],
3160-
# self.show_no_themes_message,
3161-
# self.show_recording_start_message,
3162-
# self.remote_control_server_ip,
3163-
# self.remote_control_server_port,
3164-
# self.remote_control_server_auto_start
3165-
# ))
31663154

31673155
''' write extra player parameters to file '''
31683156
if path.exists(self.player_params_file):

pyradio/config_window.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
class PyRadioConfigWindow():
4242
_title = 'PyRadio Configuration'
4343

44+
_tts_volume_date = (0, 0, 0)
4445
_help_text = []
4546
_help_text.append(None)
4647
_help_text.append(['Specify the player to use with PyRadio, or the player detection order.', '|',
@@ -80,7 +81,15 @@ class PyRadioConfigWindow():
8081
_help_text.append(['Notice: Not applicable on Windows!', '|', 'Online Radio Directory Services (like RadioBrowser) will usually provide an icon for the stations they advertise.', '|', 'PyRadio can use this icon (provided that one exists and is of JPG or PNG format) while displaying Desktop Notifications.', '|', 'Setting this option to True, will enable the behavior above.', '|', 'If this option is False, the default icon will be used.', '|', 'Default value: True'])
8182
_help_text.append(['Notice: Not applicable on Windows!', '|', 'If the previous option is enabled, Stations Icons will be cached.', '|', 'If this option is set to True, all icons will be deleted at program exit.', '|', 'If set to False, the icons will be available for future use.', '|', 'Default value: True'])
8283
_help_text.append(None)
83-
_help_text.append(['PyRadio now features comprehensive Text-to-Speech (TTS) support, providing auditory feedback for an enhanced radio streaming experience.', '|', 'This system delivers contextual information about station navigation, playback status, and system events.', '|', 'Default value: False'])
84+
_help_text.append(['PyRadio now features comprehensive Text-to-Speech (TTS) support, providing auditory feedback for an enhanced radio streaming experience.', '|', 'This system delivers contextual information about station navigation, playback status, and system events.', '|', 'The TTS function cal also be termporarily toggled by pressing ' + to_str('open_extra') + to_str('toggle_tts') + '.', '|', 'Default value: False'])
85+
if platform.startswith('dar'):
86+
_help_text.append(['This option will not be used by the TTS engine.', '|', 'Adjust the System Volume instead.'])
87+
elif platform.startswith('win'):
88+
_help_text.append(['This is the volume to be used by the TTS engine.', '|', 'Valid values: 0 (silent) - 100', '|', 'Default value: 50'])
89+
_tts_volume_date = (0, 100, 5)
90+
else:
91+
_help_text.append(['This is the volume to be used by the speech dispatcher, provided that the engine selected supports it.', '|', 'Valid values: -100,+100', '|', 'Default value: 50'])
92+
_tts_volume_date = (-100, 100, 10)
8493
_help_text.append(None)
8594
_help_text.append(['If this option is enabled, the current time will be displayed at the bottom left corner of the window at program startup.', '|', 'Adjust the time format in the next option to change how the current time is displayed.', '|', r'You can always hide it by pressing ' + to_str('open_extra') + to_str('toggle_time') + '.', '|', 'Default value: False'])
8695
_help_text.append(['This is the time format to be used when the clock is visible.', '|', 'Available values are:', ' 0: 24h, with seconds', ' 1: 24h, no seconds', ' 2: 12h, with am/pm and seconds', ' 3: 12h, no am/pm, with seconds', ' 4: 12h, with am/pm, no seconds', ' 5: 12h, no am/pm, no seconds', '|', 'Default value: 1'])
@@ -813,6 +822,7 @@ def keypress(self, char):
813822
'time_format',
814823
'buffering',
815824
'mplayer_save_br',
825+
'tts_volume',
816826
) and char in (
817827
curses.KEY_LEFT,
818828
curses.KEY_RIGHT,
@@ -1123,6 +1133,37 @@ def keypress(self, char):
11231133
self._win.refresh()
11241134
return -1, []
11251135

1136+
elif val[0] == 'tts_volume':
1137+
if char in (curses.KEY_RIGHT, kbkey['l']) or \
1138+
check_localized(char, (kbkey['l'], )):
1139+
t = int(val[1][1])
1140+
if t < self._tts_volume_date[1]:
1141+
t += self._tts_volume_date[2]
1142+
if t > self._tts_volume_date[1]:
1143+
t = self._tts_volume_date[1]
1144+
self._config_options[val[0]][1] = str(t)
1145+
self._win.addstr(
1146+
Y, 3 + len(val[1][0]),
1147+
str(t) + ' ', curses.color_pair(6))
1148+
self._print_title()
1149+
self._win.refresh()
1150+
return -1, []
1151+
1152+
elif char in (curses.KEY_LEFT, kbkey['h']) or \
1153+
check_localized(char, (kbkey['h'], )):
1154+
t = int(val[1][1])
1155+
if t > self._tts_volume_date[0]:
1156+
t -= self._tts_volume_date[2]
1157+
if t < self._tts_volume_date[0]:
1158+
t = self._tts_volume_date[0]
1159+
self._config_options[val[0]][1] = str(t)
1160+
self._win.addstr(
1161+
Y, 3 + len(val[1][0]),
1162+
str(t) + ' ', curses.color_pair(6))
1163+
self._print_title()
1164+
self._win.refresh()
1165+
return -1, []
1166+
11261167
if char in (curses.KEY_ENTER, ord('\n'), ord('\r'),
11271168
kbkey['pause'], kbkey['l'], curses.KEY_RIGHT) or \
11281169
check_localized(char, (kbkey['l'], kbkey['pause'])):

pyradio/install.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
''' This is PyRadio version this
1818
install.py was released for
1919
'''
20-
PyRadioInstallPyReleaseVersion = '0.9.3.11.20'
20+
PyRadioInstallPyReleaseVersion = '0.9.3.11.21'
2121

2222
locale.setlocale(locale.LC_ALL, "")
2323

pyradio/messages_system.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ def set_text(self, parent, *args):
749749
|{}|'s configuration has been altered
750750
but not saved. Do you want to save it now?
751751
752-
Press |{y}| to save it or |n| to disregard it.
752+
Press |{y}| to save it or |{n}| to disregard it.
753753
'''
754754
)),
755755

@@ -1730,8 +1730,10 @@ def set_text(self, parent, *args):
17301730
pass
17311731
self.simple_dialog = False
17321732
self._txt = {}
1733+
return text
17331734

1734-
def __init__(self, config, op_mode, prev_op_mode):
1735+
def __init__(self, config, op_mode, prev_op_mode, speak_high):
1736+
self._speak_high = speak_high
17351737
self._args = None
17361738
self._txt = None
17371739
self._active_token = None

pyradio/radio.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ def __init__(self, pyradio_config,
513513
do not display any message
514514
'''
515515

516+
self._tts_volume = pyradio_config.tts_volume
516517
self._enable_tts = pyradio_config.enable_tts
517518
self.ws = Window_Stack(self._speak_selection)
518519
self.player = None
@@ -869,7 +870,8 @@ def __init__(self, pyradio_config,
869870
self._messaging_win = PyRadioMessagesSystem(
870871
self._cnf,
871872
lambda: self.ws.operation_mode,
872-
lambda: self.ws.previous_operation_mode
873+
lambda: self.ws.previous_operation_mode,
874+
lambda: self._speak_high
873875
)
874876

875877
''' keep resource opener from Opener Selection window.
@@ -2134,7 +2136,7 @@ def run(self):
21342136
except KeyboardInterrupt:
21352137
pass
21362138
else:
2137-
self.tts = TTSManager(self._cnf.enable_tts)
2139+
self.tts = TTSManager(self._cnf.enable_tts, volume=lambda: self._tts_volume)
21382140
''' start update detection and notification thread '''
21392141
if CAN_CHECK_FOR_UPDATES:
21402142
if self._cnf.locked:
@@ -3344,7 +3346,9 @@ def _open_simple_message_by_key(self, *args):
33443346

33453347
def _open_message_win_by_key(self, *args):
33463348
# logger.error('args = "{}"'.format(args))
3347-
self._messaging_win.set_text(self.bodyWin, *args)
3349+
text = self._messaging_win.set_text(self.bodyWin, *args)
3350+
# self._speak_high(''.join(text).replace('|', '').replace('ESC', 'escape'))
3351+
logger.error('\n\ntext =\n{}\n\n'.format(text))
33483352
self.ws.operation_mode = self._message_system_default_operation_mode
33493353
self._messaging_win.show()
33503354

@@ -7279,7 +7283,7 @@ def keypress(self, char):
72797283
self.tts.set_enabled(False)
72807284
sleep(0.1)
72817285
self.tts = None
7282-
self.tts = TTSManager(enabled=True)
7286+
self.tts = TTSManager(enabled=True, volume=lambda: self._tts_volume)
72837287
# self.tts.set_enabled(self._enable_tts)
72847288
if self._enable_tts:
72857289
self.tts.queue_speech('T T S enabled', Priority.HIGH)
@@ -8022,6 +8026,7 @@ def keypress(self, char):
80228026
self._cnf.backup_player_params[0] = self._cnf.params[self._cnf.PLAYER_NAME][:]
80238027
ret = self._cnf.save_config()
80248028
if ret == -1:
8029+
# self._speak_high(msg[0])
80258030
''' Error saving config '''
80268031
if self.player.isPlaying():
80278032
self.stopPlayer()
@@ -8030,6 +8035,9 @@ def keypress(self, char):
80308035
self.log.write(msg_id=STATES.ANY, msg=msg[0], help_msg=False, suffix=self._status_suffix)
80318036
self._print_config_save_error()
80328037
elif ret == 0:
8038+
if self._enable_tts:
8039+
self.tts.queue_speech('Config saved successfully', Priority.HIGH)
8040+
# self._speak_high(msg[1])
80338041
''' Config saved successfully '''
80348042

80358043
''' update functions dicts '''
@@ -8152,6 +8160,18 @@ def keypress(self, char):
81528160
self._cnf.buffering_data = []
81538161
else:
81548162
self._update_config_buffering_data(reset=False)
8163+
8164+
# update TTS
8165+
logger.error('\n\n\n')
8166+
logger.error(f'{self._cnf.enable_tts = }')
8167+
logger.error(f'{self._enable_tts = }')
8168+
logger.error('\n\n\n')
8169+
if self._enable_tts != self._cnf.enable_tts:
8170+
self._enable_tts = self._cnf.enable_tts
8171+
self.tts.set_enabled(self._enable_tts)
8172+
if self._enable_tts:
8173+
self.tts.queue_speech('Config saved successfully', Priority.HIGH)
8174+
self._tts_volume = self._cnf.tts_volume
81558175
elif ret == 1:
81568176
''' config not modified '''
81578177
self._show_notification_with_delay(

0 commit comments

Comments
 (0)