diff --git a/src/calibre/utils/windows/winspeech.cpp b/src/calibre/utils/windows/winspeech.cpp index df5a688e7c..f5333169f1 100644 --- a/src/calibre/utils/windows/winspeech.cpp +++ b/src/calibre/utils/windows/winspeech.cpp @@ -665,6 +665,19 @@ handle_save(id_type cmd_id, std::vector &parts) { typedef std::function, int64_t*)> handler_function; +static DeviceInformationKind +get_device_kind(const std::wstring x) { + if (x == L"device") return DeviceInformationKind::Device; + if (x == L"association_endpoint") return DeviceInformationKind::AssociationEndpoint; + if (x == L"association_endpoint_container") return DeviceInformationKind::AssociationEndpointContainer; + if (x == L"association_endpoint_service") return DeviceInformationKind::AssociationEndpointService; + if (x == L"device_container") return DeviceInformationKind::DeviceContainer; + if (x == L"device_interface") return DeviceInformationKind::DeviceInterface; + if (x == L"device_interface_class") return DeviceInformationKind::DeviceInterfaceClass; + if (x == L"device_panel") return DeviceInformationKind::DevicePanel; + return DeviceInformationKind::Unknown; +} + static const std::unordered_map handlers = { {"exit", [](id_type cmd_id, std::vector parts, int64_t* exit_code) { @@ -689,7 +702,9 @@ static const std::unordered_map handlers = { }}, {"state", [](id_type cmd_id, std::vector parts, int64_t*) { - output(cmd_id, "state", {{"playback_state", media_player.PlaybackSession().PlaybackState()}}); + auto ps = media_player.PlaybackSession(); + if (ps) output(cmd_id, "state", {{"playback_state", ps.PlaybackState()}}); + else output(cmd_id, "state", {{"playback_state", ""}}); }}, {"default_voice", [](id_type cmd_id, std::vector parts, int64_t*) { @@ -709,17 +724,26 @@ static const std::unordered_map handlers = { }}, {"audio_device", [](id_type cmd_id, std::vector parts, int64_t*) { + bool found = false; if (parts.size()) { - auto di = DeviceInformation::CreateFromIdAsync(parts[0]).get(); - media_player.AudioDevice(di); + auto device_kind = std::wstring(parts.at(0)); + parts.erase(parts.begin(), parts.begin() + 1); + auto device_id = join(parts); + auto di = DeviceInformation::CreateFromIdAsync(device_id, {}, get_device_kind(device_kind)).get(); + if (di) { + media_player.AudioDevice(di); + found = true; + } } - output(cmd_id, "audio_device", {{"value", media_player.AudioDevice()}}); + auto x = media_player.AudioDevice(); + if (x) output(cmd_id, "audio_device", {{"value", x}, {"found", found}}); + else output(cmd_id, "audio_device", {{"value", ""}, {"found", found}}); }}, {"voice", [](id_type cmd_id, std::vector parts, int64_t*) { bool found = false; if (parts.size()) { - auto voice_id = winrt::hstring(parts[0]); + auto voice_id = winrt::hstring(parts.at(0)); for (auto const &candidate : SpeechSynthesizer::AllVoices()) { if (candidate.Id() == voice_id) { speech_synthesizer.Voice(candidate); @@ -728,12 +752,14 @@ static const std::unordered_map handlers = { } } } - output(cmd_id, "voice", {{"value", speech_synthesizer.Voice()}, {"found", found}}); + auto x = speech_synthesizer.Voice(); + if (x) output(cmd_id, "voice", {{"value", speech_synthesizer.Voice()}, {"found", found}}); + else output(cmd_id, "voice", {{"value", ""}, {"found", found}}); }}, {"volume", [](id_type cmd_id, std::vector parts, int64_t*) { if (parts.size()) { - auto vol = parse_double(parts[0].data()); + auto vol = parse_double(parts.at(0).data()); if (vol < 0 || vol > 1) throw std::out_of_range("Invalid volume value must be between 0 and 1"); speech_synthesizer.Options().AudioVolume(vol); } @@ -742,7 +768,7 @@ static const std::unordered_map handlers = { {"rate", [](id_type cmd_id, std::vector parts, int64_t*) { if (parts.size()) { - auto rate = parse_double(parts[0].data()); + auto rate = parse_double(parts.at(0).data()); if (rate < 0.5 || rate > 6.0) throw std::out_of_range("Invalid rate value must be between 0.5 and 6"); speech_synthesizer.Options().SpeakingRate(rate); } @@ -751,7 +777,7 @@ static const std::unordered_map handlers = { {"pitch", [](id_type cmd_id, std::vector parts, int64_t*) { if (parts.size()) { - auto pitch = parse_double(parts[0].data()); + auto pitch = parse_double(parts.at(0).data()); if (pitch < 0 || pitch > 2) throw std::out_of_range("Invalid pitch value must be between 0 and 2"); speech_synthesizer.Options().AudioPitch(pitch); } diff --git a/src/calibre/utils/windows/winspeech.py b/src/calibre/utils/windows/winspeech.py index 086222606c..d58fcb936e 100644 --- a/src/calibre/utils/windows/winspeech.py +++ b/src/calibre/utils/windows/winspeech.py @@ -133,3 +133,23 @@ def develop_save(text='Lucca Brazzi sleeps with the fishes.', filename="speech.w with SharedMemory(size=max_buffer_size(text)) as shm: sz = encode_to_file_object(text, shm) develop_loop(f'2 save {st} {sz} {shm.name} {filename}', 2) + + +def develop_interactive(): + import subprocess + from calibre.debug import run_calibre_debug + print('\x1b[32mInteractive winspeech', '\x1b[39m]]'[:-2], flush=True) + p = run_calibre_debug('-c', 'from calibre_extensions.winspeech import run_main_loop; raise SystemExit(run_main_loop())', + stdin=subprocess.PIPE) + try: + while True: + line = input() + if p.poll() is not None: + raise SystemExit(p.returncode) + p.stdin.write((line + '\n').encode()) + p.stdin.flush() + except KeyboardInterrupt: + print('Exiting on interrupt', flush=True) + finally: + if p.poll() is None: + p.kill()