From 9c703b781dfeb37380059f41b053f18466690eaf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Oct 2020 14:25:51 +0530 Subject: [PATCH] Code to set the sound output --- src/calibre/utils/windows/winsapi.cpp | 35 ++++++++++++++++++++++++++- src/calibre/utils/windows/winsapi.py | 16 +++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/calibre/utils/windows/winsapi.cpp b/src/calibre/utils/windows/winsapi.cpp index 60d8df4337..f501da4a28 100644 --- a/src/calibre/utils/windows/winsapi.cpp +++ b/src/calibre/utils/windows/winsapi.cpp @@ -84,6 +84,37 @@ Voice_get_all_sound_outputs(Voice *self, PyObject *args) { return PyList_AsTuple(ans.ptr()); } +static PyObject* +Voice_get_current_sound_output(Voice *self, PyObject *args) { + HRESULT hr = S_OK; + CComPtr token = NULL; + if (FAILED(hr = self->voice->GetOutputObjectToken(&token))) return error_from_hresult(hr, "Failed to get current output object token"); + if (hr == S_FALSE) Py_RETURN_NONE; + com_wchar_raii id; + if (FAILED(hr = token->GetId(id.address()))) return error_from_hresult(hr, "Failed to get ID for current audio output token"); + return PyUnicode_FromWideChar(id.ptr(), -1); +} + +static PyObject* +Voice_set_current_sound_output(Voice *self, PyObject *args) { + wchar_raii id; + int allow_format_changes = 1; + if (!PyArg_ParseTuple(args, "|O&p", py_to_wchar, &id, &allow_format_changes)) return NULL; + HRESULT hr = S_OK; + if (id) { + CComPtr token = NULL; + if (FAILED(hr = SpGetTokenFromId(id.ptr(), &token))) { + return error_from_hresult(hr, "Failed to find sound output with id", PyTuple_GET_ITEM(args, 0)); + } + if (FAILED(hr = self->voice->SetOutput(token, allow_format_changes))) return error_from_hresult(hr, "Failed to set sound output to", PyTuple_GET_ITEM(args, 0)); + + } else { + if (FAILED(hr = self->voice->SetOutput(NULL, allow_format_changes))) return error_from_hresult(hr, "Failed to set sound output to default"); + } + Py_RETURN_NONE; +} + + static PyObject* Voice_get_current_voice(Voice *self, PyObject *args) { HRESULT hr = S_OK; @@ -106,7 +137,7 @@ Voice_set_current_voice(Voice *self, PyObject *args) { if (FAILED(hr = SpGetTokenFromId(id.ptr(), &token))) { return error_from_hresult(hr, "Failed to find voice with id", PyTuple_GET_ITEM(args, 0)); } - if (FAILED(hr = self->voice->SetVoice(token))) return error_from_hresult(hr, "Failed to set voice to default"); + if (FAILED(hr = self->voice->SetVoice(token))) return error_from_hresult(hr, "Failed to set voice to", PyTuple_GET_ITEM(args, 0)); } else { if (FAILED(hr = self->voice->SetVoice(NULL))) return error_from_hresult(hr, "Failed to set voice to default"); } @@ -168,7 +199,9 @@ Voice_get_all_voices(Voice *self, PyObject *args) { static PyMethodDef Voice_methods[] = { M(get_all_voices, METH_NOARGS), M(get_current_voice, METH_NOARGS), + M(get_current_sound_output, METH_NOARGS), M(set_current_voice, METH_VARARGS), + M(set_current_sound_output, METH_VARARGS), M(get_all_sound_outputs, METH_NOARGS), {NULL, NULL, 0, NULL} }; diff --git a/src/calibre/utils/windows/winsapi.py b/src/calibre/utils/windows/winsapi.py index 11a0985f37..e50d9fecb4 100644 --- a/src/calibre/utils/windows/winsapi.py +++ b/src/calibre/utils/windows/winsapi.py @@ -32,13 +32,27 @@ def find_tests(): self.assertTrue(all_voices) self.assertIn(default_voice, {x['id'] for x in all_voices}) for voice in all_voices: - for key in ('name', 'gender', 'age', 'language'): + for key in ('name', 'gender', 'age', 'language', 'description'): self.assertIn(key, voice) self.sapi.set_current_voice(voice['id']) self.assertEqual(self.sapi.get_current_voice(), voice['id']) self.sapi.set_current_voice() self.assertEqual(self.sapi.get_current_voice(), default_voice) + def test_enumeration_of_sound_outputs(self): + default_output = self.sapi.get_current_sound_output() + self.assertTrue(default_output) + all_outputs = self.sapi.get_all_sound_outputs() + self.assertTrue(all_outputs) + self.assertIn(default_output, {x['id'] for x in all_outputs}) + for output in all_outputs: + for key in ('id', 'description',): + self.assertIn(key, output) + self.sapi.set_current_voice(output['id']) + self.assertEqual(self.sapi.get_current_sound_output(), output['id']) + self.sapi.set_current_sound_output() + self.assertEqual(self.sapi.get_current_sound_output(), default_output) + return unittest.defaultTestLoader.loadTestsFromTestCase(TestSAPI)