From ea0eeaf81e5a502b5f10f9e9a0ed8bd2131df95a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Oct 2020 14:47:21 +0530 Subject: [PATCH] Code to get and set rate and volume --- src/calibre/utils/windows/winsapi.cpp | 47 ++++++++++++++++++++++++++- src/calibre/utils/windows/winsapi.py | 16 +++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/calibre/utils/windows/winsapi.cpp b/src/calibre/utils/windows/winsapi.cpp index f501da4a28..af88fd99b2 100644 --- a/src/calibre/utils/windows/winsapi.cpp +++ b/src/calibre/utils/windows/winsapi.cpp @@ -54,6 +54,8 @@ Voice_dealloc(Voice *self) { CoUninitialize(); } + +// Enumeration {{{ static PyObject* Voice_get_all_sound_outputs(Voice *self, PyObject *args) { HRESULT hr = S_OK; @@ -193,16 +195,59 @@ Voice_get_all_voices(Voice *self, PyObject *args) { } return PyList_AsTuple(ans.ptr()); } +// }}} +// Volume and rate {{{ +static PyObject* +Voice_get_current_volume(Voice *self, PyObject *args) { + HRESULT hr = S_OK; + USHORT volume; + if (FAILED(hr = self->voice->GetVolume(&volume))) return error_from_hresult(hr); + return PyLong_FromUnsignedLong((unsigned long)volume); +} + +static PyObject* +Voice_get_current_rate(Voice *self, PyObject *args) { + HRESULT hr = S_OK; + long rate; + if (FAILED(hr = self->voice->GetRate(&rate))) return error_from_hresult(hr); + return PyLong_FromLong(rate); +} + +static PyObject* +Voice_set_current_rate(Voice *self, PyObject *args) { + HRESULT hr = S_OK; + long rate; + if (!PyArg_ParseTuple(args, "l", &rate)) return NULL; + if (rate < -10 || rate > 10) { PyErr_SetString(PyExc_ValueError, "rate must be between -10 and 10"); return NULL; } + if (FAILED(hr = self->voice->SetRate(rate))) return error_from_hresult(hr); + Py_RETURN_NONE; +} + +static PyObject* +Voice_set_current_volume(Voice *self, PyObject *args) { + HRESULT hr = S_OK; + unsigned short volume; + if (!PyArg_ParseTuple(args, "H", &volume)) return NULL; + if (FAILED(hr = self->voice->SetVolume(volume))) return error_from_hresult(hr); + Py_RETURN_NONE; +} +// }}} + #define M(name, args) { #name, (PyCFunction)Voice_##name, args, ""} static PyMethodDef Voice_methods[] = { M(get_all_voices, METH_NOARGS), + M(get_all_sound_outputs, METH_NOARGS), + + M(get_current_rate, METH_NOARGS), + M(get_current_volume, METH_NOARGS), M(get_current_voice, METH_NOARGS), M(get_current_sound_output, METH_NOARGS), M(set_current_voice, METH_VARARGS), + M(set_current_rate, METH_VARARGS), + M(set_current_volume, METH_VARARGS), M(set_current_sound_output, METH_VARARGS), - M(get_all_sound_outputs, METH_NOARGS), {NULL, NULL, 0, NULL} }; #undef M diff --git a/src/calibre/utils/windows/winsapi.py b/src/calibre/utils/windows/winsapi.py index e50d9fecb4..adb9425271 100644 --- a/src/calibre/utils/windows/winsapi.py +++ b/src/calibre/utils/windows/winsapi.py @@ -16,6 +16,8 @@ def develop(): def find_tests(): import unittest + import os + is_ci = os.environ.get('CI', '').lower() == 'true' class TestSAPI(unittest.TestCase): @@ -39,6 +41,7 @@ def find_tests(): self.sapi.set_current_voice() self.assertEqual(self.sapi.get_current_voice(), default_voice) + @unittest.skipIf(is_ci, 'No sound output on CI') def test_enumeration_of_sound_outputs(self): default_output = self.sapi.get_current_sound_output() self.assertTrue(default_output) @@ -53,6 +56,19 @@ def find_tests(): self.sapi.set_current_sound_output() self.assertEqual(self.sapi.get_current_sound_output(), default_output) + def test_volume_and_rate(self): + dr = self.sapi.get_current_rate() + new_rate = dr // 2 + 1 + self.sapi.set_current_rate(new_rate) + self.assertEqual(self.sapi.get_current_rate(), new_rate) + self.sapi.set_current_rate(dr) + + dv = self.sapi.get_current_volume() + new_vol = dv // 2 + 3 + self.sapi.set_current_volume(new_vol) + self.assertEqual(self.sapi.get_current_volume(), new_vol) + self.sapi.set_current_volume(dv) + return unittest.defaultTestLoader.loadTestsFromTestCase(TestSAPI)