mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Implement getting and setting current voice
This commit is contained in:
parent
ee374f5d93
commit
61d44ea95e
@ -13,11 +13,13 @@
|
||||
#include <comdef.h>
|
||||
|
||||
static inline PyObject*
|
||||
set_error_from_hresult(const char *file, const int line, const HRESULT hr, const char *prefix="") {
|
||||
set_error_from_hresult(const char *file, const int line, const HRESULT hr, const char *prefix="", PyObject *name=NULL) {
|
||||
_com_error err(hr);
|
||||
LPCWSTR msg = err.ErrorMessage();
|
||||
PyObject *pmsg = PyUnicode_FromWideChar(msg, -1);
|
||||
PyObject *ans = PyErr_Format(PyExc_OSError, "%s:%d:%s:%V", file, line, prefix, pmsg, "Out of memory");
|
||||
PyObject *ans;
|
||||
if (name) ans = PyErr_Format(PyExc_OSError, "%s:%d:%s:%V: %S", file, line, prefix, pmsg, "Out of memory", name);
|
||||
else ans = PyErr_Format(PyExc_OSError, "%s:%d:%s:%V", file, line, prefix, pmsg, "Out of memory");
|
||||
Py_CLEAR(pmsg);
|
||||
return ans;
|
||||
}
|
||||
@ -41,6 +43,7 @@ class wchar_raii {
|
||||
|
||||
wchar_t *ptr() { return handle; }
|
||||
void set_ptr(wchar_t *val) { handle = val; }
|
||||
explicit operator bool() const { return handle != NULL; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -81,7 +81,36 @@ Voice_get_all_sound_outputs(Voice *self, PyObject *args) {
|
||||
|
||||
if (PyList_Append(ans.ptr(), dict.ptr()) != 0) return NULL;
|
||||
}
|
||||
return ans.detach();
|
||||
return PyList_AsTuple(ans.ptr());
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
Voice_get_current_voice(Voice *self, PyObject *args) {
|
||||
HRESULT hr = S_OK;
|
||||
CComPtr<ISpObjectToken> token = NULL;
|
||||
if (FAILED(hr = self->voice->GetVoice(&token))) {
|
||||
return error_from_hresult(hr, "Failed to get current voice");
|
||||
}
|
||||
com_wchar_raii id;
|
||||
if (FAILED(hr = token->GetId(id.address()))) return error_from_hresult(hr, "Failed to get ID for current voice");
|
||||
return PyUnicode_FromWideChar(id.ptr(), -1);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
Voice_set_current_voice(Voice *self, PyObject *args) {
|
||||
wchar_raii id;
|
||||
if (!PyArg_ParseTuple(args, "|O&", py_to_wchar, &id)) return NULL;
|
||||
HRESULT hr = S_OK;
|
||||
if (id) {
|
||||
CComPtr<ISpObjectToken> token = NULL;
|
||||
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");
|
||||
} else {
|
||||
if (FAILED(hr = self->voice->SetVoice(NULL))) return error_from_hresult(hr, "Failed to set voice to default");
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
@ -131,13 +160,15 @@ Voice_get_all_voices(Voice *self, PyObject *args) {
|
||||
}
|
||||
if (PyList_Append(ans.ptr(), dict.ptr()) != 0) return NULL;
|
||||
}
|
||||
return ans.detach();
|
||||
return PyList_AsTuple(ans.ptr());
|
||||
}
|
||||
|
||||
|
||||
#define M(name, args) { #name, (PyCFunction)Voice_##name, args, ""}
|
||||
static PyMethodDef Voice_methods[] = {
|
||||
M(get_all_voices, METH_NOARGS),
|
||||
M(get_current_voice, METH_NOARGS),
|
||||
M(set_current_voice, METH_VARARGS),
|
||||
M(get_all_sound_outputs, METH_NOARGS),
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
@ -166,7 +197,7 @@ static struct PyModuleDef winsapi_module = {
|
||||
extern "C" {
|
||||
|
||||
CALIBRE_MODINIT_FUNC PyInit_winsapi(void) {
|
||||
VoiceType.tp_name = "winsapi.Voice";
|
||||
VoiceType.tp_name = "winsapi.ISpVoice";
|
||||
VoiceType.tp_doc = "Wrapper for ISpVoice";
|
||||
VoiceType.tp_basicsize = sizeof(Voice);
|
||||
VoiceType.tp_itemsize = 0;
|
||||
@ -180,7 +211,7 @@ CALIBRE_MODINIT_FUNC PyInit_winsapi(void) {
|
||||
if (m == NULL) return NULL;
|
||||
|
||||
Py_INCREF(&VoiceType);
|
||||
if (PyModule_AddObject(m, "Voice", (PyObject *) &VoiceType) < 0) {
|
||||
if (PyModule_AddObject(m, "ISpVoice", (PyObject *) &VoiceType) < 0) {
|
||||
Py_DECREF(&VoiceType);
|
||||
Py_DECREF(m);
|
||||
return NULL;
|
||||
|
48
src/calibre/utils/windows/winsapi.py
Normal file
48
src/calibre/utils/windows/winsapi.py
Normal file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from calibre_extensions.winsapi import ISpVoice
|
||||
|
||||
|
||||
def develop():
|
||||
from pprint import pprint
|
||||
spv = ISpVoice()
|
||||
voices = spv.get_all_voices()
|
||||
pprint(voices)
|
||||
for voice in voices:
|
||||
spv.set_current_voice(voice['id'])
|
||||
|
||||
|
||||
def find_tests():
|
||||
import unittest
|
||||
|
||||
class TestSAPI(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.sapi = ISpVoice()
|
||||
|
||||
def tearDown(self):
|
||||
self.sapi = None
|
||||
|
||||
def test_enumeration_of_voices(self):
|
||||
default_voice = self.sapi.get_current_voice()
|
||||
self.assertTrue(default_voice)
|
||||
all_voices = self.sapi.get_all_voices()
|
||||
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'):
|
||||
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)
|
||||
|
||||
return unittest.defaultTestLoader.loadTestsFromTestCase(TestSAPI)
|
||||
|
||||
|
||||
def run_tests():
|
||||
from calibre.utils.run_tests import run_tests
|
||||
run_tests(find_tests)
|
Loading…
x
Reference in New Issue
Block a user