Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
403 results
Show changes
Commits on Source (13)
......@@ -2,9 +2,11 @@
* sapi.cpp: Simple text to Speech renderer for Windows, based on SAPI
*****************************************************************************
* Copyright (c) 2015 Moti Zilberman
* Copyright (c) 2023 Videolabs
*
* Authors: Moti Zilberman
* Jean-Baptiste Kempf
* Alexandre Janniaux <ajanni@videolabs.io>
*
* The MIT License (MIT)
*
......@@ -37,21 +39,30 @@
#include <vlc_filter.h>
#include <vlc_charset.h>
#include <vlc_subpicture.h>
#include <vlc_threads.h>
#include <vlc_cxx_helpers.hpp>
#include <windows.h>
#include <sapi.h>
#include <sphelper.h>
#include <initguid.h>
#include <memory>
// not available in standard libraries and used in inline functions without __uuidof()
DEFINE_GUID(CLSID_SpObjectTokenCategory, 0xa910187f, 0x0c7a, 0x45ac, 0x92,0xcc, 0x59,0xed,0xaf,0xb7,0x7b,0x53);
extern "C" {
static int Create (filter_t *);
static void Destroy(filter_t *);
static int RenderText(filter_t *,
subpicture_region_t *,
subpicture_region_t *,
const vlc_fourcc_t *);
}
static int RenderTextMTA(filter_t *, subpicture_region_t *);
vlc_module_begin ()
set_description(N_("Speech synthesis for Windows"))
......@@ -60,175 +71,264 @@ vlc_module_begin ()
set_callback_text_renderer(Create, 0)
/* Note: Skip label translation - too technical */
add_integer("sapi-voice", -1, "Voice Index", nullptr)
add_integer("sapi-voice", -1, "Voice Index", "Voice Index")
vlc_module_end ()
struct filter_sys_t
namespace {
enum class FilterCommand {
CMD_RENDER_TEXT,
CMD_EXIT,
};
struct filter_sapi
{
/* We need a MTA thread, so we ensure it's possible by creating a
* dedicated thread for that mode. */
vlc_thread_t thread;
ISpVoice* cpVoice;
char* lastString;
bool initialized;
vlc::threads::semaphore sem_ready;
vlc::threads::semaphore cmd_available;
vlc::threads::semaphore cmd_ready;
struct {
FilterCommand query;
union {
struct {
int result;
subpicture_region_t *region;
} render_text;
};
} cmd;
};
/* MTA functions */
static int TryEnterMTA(vlc_object_t *obj)
struct MTAGuard
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (unlikely(FAILED(hr)))
HRESULT result_mta;
MTAGuard()
{
msg_Err (obj, "cannot initialize COM (error 0x%lX)", hr);
return -1;
this->result_mta = CoInitializeEx(NULL, COINIT_MULTITHREADED);
}
return 0;
}
#define TryEnterMTA(o) TryEnterMTA(VLC_OBJECT(o))
static void EnterMTA(void)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (unlikely(FAILED(hr)))
abort();
}
~MTAGuard()
{
if (SUCCEEDED(this->result_mta))
CoUninitialize();
}
static void LeaveMTA(void)
{
CoUninitialize();
};
}
static const struct FilterOperationInitializer {
struct vlc_filter_operations ops {};
FilterOperationInitializer()
{
ops.render = RenderText;
ops.close = Destroy;
};
} filter_ops;
static int Create (filter_t *p_filter)
static int RenderTextMTA(filter_t *p_filter,
subpicture_region_t * p_region_in)
{
filter_sys_t *p_sys;
HRESULT hr;
struct filter_sapi *p_sys = static_cast<struct filter_sapi *>( p_filter->p_sys );
text_segment_t *p_segment = p_region_in->p_text;
if (TryEnterMTA(p_filter))
if (!p_segment)
return VLC_EGENERIC;
p_filter->p_sys = p_sys = (filter_sys_t*) malloc(sizeof(filter_sys_t));
if (!p_sys)
goto error;
for (const text_segment_t *s = p_segment; s != NULL; s = s->p_next ) {
if (!s->psz_text)
continue;
p_sys->cpVoice = NULL;
p_sys->lastString = NULL;
if (strlen(s->psz_text) == 0)
continue;
hr = CoCreateInstance(__uuidof(SpVoice), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&p_sys->cpVoice));
if (SUCCEEDED(hr)) {
ISpObjectToken* cpVoiceToken = NULL;
IEnumSpObjectTokens* cpEnum = NULL;
ULONG ulCount = 0;
if (p_sys->lastString && !strcmp(p_sys->lastString, s->psz_text))
continue;
hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);
if (SUCCEEDED(hr))
{
// Get the number of voices.
hr = cpEnum->GetCount(&ulCount);
if (SUCCEEDED (hr))
{
int voiceIndex = var_InheritInteger(p_filter, "sapi-voice");
if (voiceIndex > -1)
{
if ((unsigned)voiceIndex < ulCount) {
hr = cpEnum->Item(voiceIndex, &cpVoiceToken);
if (SUCCEEDED(hr)) {
hr = p_sys->cpVoice->SetVoice(cpVoiceToken);
if (SUCCEEDED(hr)) {
msg_Dbg(p_filter, "Selected voice %d", voiceIndex);
}
else {
msg_Err(p_filter, "Failed to set voice %d", voiceIndex);
}
cpVoiceToken->Release();
cpVoiceToken = NULL;
}
}
else
msg_Err(p_filter, "Voice index exceeds available count");
}
}
cpEnum->Release();
/* Set Output */
hr = p_sys->cpVoice->SetOutput(NULL, TRUE);
}
}
else
{
msg_Err(p_filter, "Could not create SpVoice");
goto error;
}
if (!strcmp(s->psz_text, "\n"))
continue;
LeaveMTA();
/* */
free(p_sys->lastString);
p_sys->lastString = strdup(s->psz_text);
p_filter->ops = &filter_ops.ops;
/* */
if (p_sys->lastString == nullptr)
continue;
return VLC_SUCCESS;
msg_Dbg(p_filter, "Speaking '%s'", s->psz_text);
error:
LeaveMTA();
free(p_sys);
return VLC_EGENERIC;
wchar_t* wideText = ToWide(s->psz_text);
HRESULT hr = p_sys->cpVoice->Speak(wideText, SPF_ASYNC, NULL);
free(wideText);
if (!SUCCEEDED(hr))
msg_Err(p_filter, "Speak() error");
}
return VLC_SUCCESS;
}
static void Destroy(filter_t *p_filter)
static int SelectVoice(filter_t *filter, ISpVoice* cpVoice)
{
filter_sys_t *p_sys = reinterpret_cast<filter_sys_t *>( p_filter->p_sys );
HRESULT hr;
ISpObjectToken* cpVoiceToken = NULL;
IEnumSpObjectTokens* cpEnum = NULL;
ULONG ulCount = 0;
int voiceIndex = var_InheritInteger(filter, "sapi-voice");
if (voiceIndex < 0)
return 0;
if (p_sys->cpVoice)
p_sys->cpVoice->Release();
hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);
if (!SUCCEEDED(hr))
return -ENOENT;
free(p_sys->lastString);
free(p_sys);
// Get the number of voices.
hr = cpEnum->GetCount(&ulCount);
if (!SUCCEEDED (hr))
goto error;
if ((unsigned)voiceIndex >= ulCount) {
msg_Err(filter, "Voice index exceeds available count");
cpEnum->Release();
return -EINVAL;
}
hr = cpEnum->Item(voiceIndex, &cpVoiceToken);
if (!SUCCEEDED(hr))
goto error;
hr = cpVoice->SetVoice(cpVoiceToken);
if (SUCCEEDED(hr)) {
msg_Dbg(filter, "Selected voice %d", voiceIndex);
}
else {
msg_Err(filter, "Failed to set voice %d", voiceIndex);
}
cpVoiceToken->Release();
cpVoiceToken = NULL;
cpEnum->Release();
return voiceIndex;
error:
cpEnum->Release();
return -ENOENT;
}
static int RenderText(filter_t *p_filter,
subpicture_region_t *,
subpicture_region_t *p_region_in,
subpicture_region_t *region_in,
const vlc_fourcc_t *)
{
filter_sys_t *p_sys = reinterpret_cast<filter_sys_t *>( p_filter->p_sys );
text_segment_t *p_segment = p_region_in->p_text;
auto *sys = static_cast<struct filter_sapi *>( p_filter->p_sys );
sys->cmd.query = FilterCommand::CMD_RENDER_TEXT;
sys->cmd.render_text.region = region_in;
sys->cmd_available.post();
sys->cmd_ready.wait();
return VLC_EGENERIC; /* We don't generate output region. */
}
if (!p_segment)
return VLC_EGENERIC;
static const struct vlc_filter_operations filter_ops = []{
struct vlc_filter_operations ops {};
ops.render = RenderText;
ops.close = Destroy;
return ops;
}();
for (const text_segment_t *s = p_segment; s != NULL; s = s->p_next ) {
if (!s->psz_text)
continue;
static void *Run(void *opaque)
{
filter_t *filter = static_cast<filter_t *>(opaque);
filter_sapi *sys = static_cast<filter_sapi *>(filter->p_sys);
if (strlen(s->psz_text) == 0)
continue;
MTAGuard guard {};
if (FAILED(guard.result_mta))
{
sys->sem_ready.post();
return NULL;
}
if (p_sys->lastString && !strcmp(p_sys->lastString, s->psz_text))
continue;
if (!strcmp(s->psz_text, "\n"))
continue;
HRESULT hr;
hr = CoCreateInstance(__uuidof(SpVoice), NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&sys->cpVoice));
if (!SUCCEEDED(hr))
{
msg_Err(filter, "Could not create SpVoice");
sys->sem_ready.post();
return NULL;
}
/* */
free(p_sys->lastString);
p_sys->lastString = strdup(s->psz_text);
int ret = SelectVoice(filter, sys->cpVoice);
(void) ret; /* TODO: we can detect whether we set the voice or not */
/* */
if (p_sys->lastString) {
msg_Dbg(p_filter, "Speaking '%s'", s->psz_text);
EnterMTA();
wchar_t* wideText = ToWide(s->psz_text);
HRESULT hr = p_sys->cpVoice->Speak(wideText, SPF_ASYNC, NULL);
free(wideText);
if (!SUCCEEDED(hr)) {
msg_Err(p_filter, "Speak() error");
}
LeaveMTA();
sys->initialized = true;
sys->sem_ready.post();
for (;;)
{
sys->cmd_available.wait();
switch (sys->cmd.query)
{
case FilterCommand::CMD_EXIT:
return NULL;
case FilterCommand::CMD_RENDER_TEXT:
sys->cmd.render_text.result =
RenderTextMTA(filter, sys->cmd.render_text.region);
sys->cmd_ready.post();
break;
default:
vlc_assert_unreachable();
}
}
return NULL;
}
static int Create (filter_t *p_filter)
{
std::unique_ptr<filter_sapi> sys {
new (std::nothrow) filter_sapi {}
};
if (sys == nullptr)
return VLC_ENOMEM;
p_filter->ops = &filter_ops;
p_filter->p_sys = sys.get();
std::unique_ptr<filter_t, void(*)(filter_t*)> guard {
p_filter, [](filter_t *filter)
{
filter->p_sys = nullptr;
filter->ops = nullptr;
}};
int ret = vlc_clone(&sys->thread, Run, p_filter);
if (ret != VLC_SUCCESS)
return VLC_ENOMEM;
sys->sem_ready.wait();
if (!sys->initialized)
{
vlc_join(sys->thread, NULL);
return VLC_ENOTSUP;
}
guard.release(); /* No need to clean filter. */
sys.release(); /* leak to p_filter */
return VLC_SUCCESS;
}
static void Destroy(filter_t *p_filter)
{
std::unique_ptr<filter_sapi> sys {
static_cast<filter_sapi *>(p_filter->p_sys)
};
if (sys->cpVoice)
sys->cpVoice->Release();
free(sys->lastString);
sys->cmd.query = FilterCommand::CMD_EXIT;
sys->cmd_available.post();
vlc_join(sys->thread, NULL);
}