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>
|
#include <comdef.h>
|
||||||
|
|
||||||
static inline PyObject*
|
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);
|
_com_error err(hr);
|
||||||
LPCWSTR msg = err.ErrorMessage();
|
LPCWSTR msg = err.ErrorMessage();
|
||||||
PyObject *pmsg = PyUnicode_FromWideChar(msg, -1);
|
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);
|
Py_CLEAR(pmsg);
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
@ -41,6 +43,7 @@ class wchar_raii {
|
|||||||
|
|
||||||
wchar_t *ptr() { return handle; }
|
wchar_t *ptr() { return handle; }
|
||||||
void set_ptr(wchar_t *val) { handle = val; }
|
void set_ptr(wchar_t *val) { handle = val; }
|
||||||
|
explicit operator bool() const { return handle != NULL; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ extern CComModule _Module;
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
ISpVoice *voice;
|
ISpVoice *voice;
|
||||||
} Voice;
|
} Voice;
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +81,36 @@ Voice_get_all_sound_outputs(Voice *self, PyObject *args) {
|
|||||||
|
|
||||||
if (PyList_Append(ans.ptr(), dict.ptr()) != 0) return NULL;
|
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*
|
static PyObject*
|
||||||
@ -131,13 +160,15 @@ Voice_get_all_voices(Voice *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
if (PyList_Append(ans.ptr(), dict.ptr()) != 0) return NULL;
|
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, ""}
|
#define M(name, args) { #name, (PyCFunction)Voice_##name, args, ""}
|
||||||
static PyMethodDef Voice_methods[] = {
|
static PyMethodDef Voice_methods[] = {
|
||||||
M(get_all_voices, METH_NOARGS),
|
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),
|
M(get_all_sound_outputs, METH_NOARGS),
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
@ -166,7 +197,7 @@ static struct PyModuleDef winsapi_module = {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
CALIBRE_MODINIT_FUNC PyInit_winsapi(void) {
|
CALIBRE_MODINIT_FUNC PyInit_winsapi(void) {
|
||||||
VoiceType.tp_name = "winsapi.Voice";
|
VoiceType.tp_name = "winsapi.ISpVoice";
|
||||||
VoiceType.tp_doc = "Wrapper for ISpVoice";
|
VoiceType.tp_doc = "Wrapper for ISpVoice";
|
||||||
VoiceType.tp_basicsize = sizeof(Voice);
|
VoiceType.tp_basicsize = sizeof(Voice);
|
||||||
VoiceType.tp_itemsize = 0;
|
VoiceType.tp_itemsize = 0;
|
||||||
@ -180,7 +211,7 @@ CALIBRE_MODINIT_FUNC PyInit_winsapi(void) {
|
|||||||
if (m == NULL) return NULL;
|
if (m == NULL) return NULL;
|
||||||
|
|
||||||
Py_INCREF(&VoiceType);
|
Py_INCREF(&VoiceType);
|
||||||
if (PyModule_AddObject(m, "Voice", (PyObject *) &VoiceType) < 0) {
|
if (PyModule_AddObject(m, "ISpVoice", (PyObject *) &VoiceType) < 0) {
|
||||||
Py_DECREF(&VoiceType);
|
Py_DECREF(&VoiceType);
|
||||||
Py_DECREF(m);
|
Py_DECREF(m);
|
||||||
return NULL;
|
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