I'm writing to ask you some advices for a particular problem regarding SAPI engine. I have an application that can speak both to the speakers and to a WAV file. I also need some events to be aware, i.e. word boundary and end input.
m_cpVoice->SetNotifyWindowMessage(m_hWnd, TTS_MSG, 0, 0);
hr = m_cpVoice->SetInterest(SPFEI_ALL_EVENTS, SPFEI_ALL_EVENTS);
Just for test I added all events! When the engine speaks to speakers all events are triggered and sent to the m_hWnd window, but when I set output to the WAV file, none of them are sent
CSpStreamFormat fmt;
CComPtr<ISpStreamFormat> pOld;
m_cpVoice->GetOutputStream(&pOld);
fmt.AssignFormat(pOld);
SPBindToFile(file, SPFM_CREATE_ALWAYS, &m_wavStream, &fmt.FormatId(), fmt.WaveFormatExPtr());
m_cpVoice->SetOutput(m_wavStream, false);
m_cpVoice->Speak(L"Test", SPF_ASYNC, 0);
Where file is a path passed as argument.
Really this code is taken from the TTS samples found on the SAPI SDK. It seems a little bit obscure the part setting the format...
Can you help me in finding the problem? Or does anyone of you know a better way to write TTS to WAV? I can not use manager code, it should be better to use the C++ version...
Thank you very much for help
EDIT 1
This seems to be a thread problem and searching in the spuihelp.h file, that contains the SPBindToFile helper I found that it uses the CoCreateInstance() function to create the stream. Maybe this is where the ISpVoice object looses its ability to send event in its creation thread.
What do you think about that?
I adopted an on-the-fly solution that I think should be acceptable in most of the cases, In fact when you write speech on files, the major event you would be aware is the "stop" event.
So... take a look a the class definition:
#define TTS_WAV_SAVED_MSG 5000
#define TTS_WAV_ERROR_MSG 5001
class CSpeech {
public:
CSpeech(HWND); // needed for the notifications
...
private:
HWND m_hWnd;
CComPtr<ISpVoice> m_cpVoice;
...
std::thread* m_thread;
void WriteToWave();
void SpeakToWave(LPCWSTR, LPCWSTR);
};
I implemented the method SpeakToWav as follows
// Global variables (***)
LPCWSTR tMsg;
LPCWSTR tFile;
long tRate;
HWND tHwnd;
ISpObjectToken* pToken;
void CSpeech::SpeakToWave(LPCWSTR file, LPCWSTR msg) {
// Using, for example wcscpy_s:
// tMsg <- msg;
// tFile <- file;
tHwnd = m_hWnd;
m_cpVoice->GetRate(&tRate);
m_cpVoice->GetVoice(&pToken);
if(m_thread == NULL)
m_thread = new std::thread(&CSpeech::WriteToWave, this);
}
And now... take a look at the WriteToWave() method:
void CSpeech::WriteToWav() {
// create a new ISpVoice that exists only in this
// new thread, so we need to
//
// CoInitialize(...) and...
// CoCreateInstance(...)
// Now set the voice, i.e.
// rate with global tRate,
// voice token with global pToken
// output format and...
// bind the stream using tFile as I did in the
// code listed in my question
cpVoice->Speak(tMsg, SPF_PURGEBEFORESPEAK, 0);
...
Now, because we did not used the SPF_ASYNC flag the call is blocking, but because we are on a separate thread the main thread can continue. After the Speak() method finished the new thread can continue as follow:
...
if(/* Speak is went ok */)
::PostMessage(tHwn, TTS_WAV_SAVED_MSG, 0, 0);
else
::PostMessage(tHwnd, TTS_WAV_ERROR_MSG, 0, 0);
}
(***) OK! using global variables is not quite cool :) but I was going fast. Maybe using a thread with the std::reference_wrapper to pass parameters would be more elegant!
Obviously, when receiving the TTS messages you need to clean the thread for a next time call! This can be done using a CSpeech::CleanThread() method like this:
void CSpeech::CleanThread() {
m_thread->join(); // I prefer to be sure the thread has finished!
delete m_thread;
m_thread = NULL;
}
What do you think about this solution? Too complex?
Related
Have a query on timeout calling and GMainContext. It is really confusing to me
Suppose I have the codes below (a bit incomplete, just for demonstration). I use normal Pthreads to create a thread. Within the thread, I run Glib functionality and created a GMainContext (stored within l_app.context).
I then created a source to run the function check_cmd iteratively at about 1 sec interval. This callback (or could we call it a thread?) will check for command from other threads( Pthreads not shown here for update in cmd status). From here onwards, there are two specific command
One to start a looping function
The other to end the looping function
I have done and thought of two ways to create the function and set them to run iteratively.
To create another timeout
using the same method of creating check_cmd
Essentially both to me are pretty much essentially the same method, when I tried both of them. Plan A (as I called it) does not work but Plan B ...actually run at least once. So I would like to know how to fix them...
Or maybe I should use g_source_add_child_source() instead?
In Summary, my question is
when you created a new context and push it to become the default context, do all subsequent function that require main_context will refer to this context?
in a nut shell, how do you add new sources when a loop is already running, ie like my cases
lastly, it is okay to quit the main loop within the callback you have created?
Here is my pseudocode
#include <glib.h>
#include <dirent.h>
#include <errno.h>
#include <pthread.h>
#define PLAN_A 0
typedef struct
{
GMainContext *context;
GMainLoop *loop;
}_App;
static _App l_app;
guint gID;
gboolean
time_cycle(gpointer udata)
{
g_print("I AM THREADING");
return true;
}
gboolean
check_cmd_session(NULL )
{
while(alive) /// alive is a boolean value that is shared with other threads(not shown)
{
if(start)
{
/// PLAN A
//// which context does this add to ??
#if PLAN_A
g_timeout_add_seconds(10, (GSourceFunc)timeout, NULL);
#else
/// or should i use PLAN B
GSource* source = g_timeout_source_new(1000);
gID = g_source_set_callback(source,
(GSourceFunc)time_cycle,
NULL,
NULL);
g_source_attach(source, l_app.context);
#endif
}
else
{
#if PLAN_A
g_source_remove(gID);
#else
}
}
g_main_loop_quit (l_app.loop);
return FALSE;
}
void*
liveService(Info *info)
{
l_app.context = g_main_context_new ();
g_main_context_push_thread_default(l_app.context);
GSource* source = g_timeout_source_new(1000);
g_source_set_callback(source,
(GSourceFunc)check_cmd_session,
NULL,
NULL);
/// make it run
g_source_attach(source, l_app.context);
g_main_loop_run (l_app.loop);
pthread_exit(NULL);
}
int main()
{
pthread_t tid[2];
int thread_counter = 0;
err = pthread_create(&(tid[thread_counter]), NULL, &live, &info);
if (err != 0)
{
printf("\n can't create live thread :[%s]", strerror(err));
}
else
{
printf("--> Thread for Live created successfully\n");
thread_counter++;
}
/**** other threads are build not shown here */
for(int i = 0; i < 2; i++)
{
printf("Joining the %d threads \n", i);
pthread_join(tid[i],NULL);
}
return 0;
}
In Summary, my question is
when you created a new context and push it to become the default context, do all subsequent function that require main_context will
refer to this context?
Functions that are documented as using the thread-default main context will use the GMainContext which has been most recently pushed with g_main_context_push_thread_default().
Functions that are documented as using the global default main context will not. They will use the GMainContext which is created at init time and which is associated with the main thread.
g_timeout_add_seconds() is documented as using the global default main context. So you need to go with plan B if you want the timeout source to be attached to a specific GMainContext.
in a nut shell, how do you add new sources when a loop is already running, ie like my cases
g_source_attach() works when a main context is being iterated.
lastly, it is okay to quit the main loop within the callback you have created?
Yes, g_main_loop_quit() can be called at any point.
From your code, it looks like you’re not creating a new GMainLoop for each GMainContext and are instead assuming that one GMainLoop will somehow work with all GMainContexts in the process. That’s not correct. If you’re going to use GMainLoop, you need to create a new one for each GMainContext you create.
All other things aside, you might find it easier to use GLib’s threading functions rather than using pthread directly. GLib’s threading functions are portable to other platforms and a little bit easier to use. Given that you’re already linking to libglib, using them would cost nothing extra.
my scenario is simple:i made a game using cocos2d-x and i want to download images (FB and Google play) for multi player users and show them once the download is done as texture for a button.
in ideal world, things work as expected.
things get tricky when those buttons got deleted before the download is done.
so the callback function is in weird state and then i get signal 11 (SIGSEGV), code 1 (SEGV_MAPERR)
and the app crashes
This is how i implmented it
I have a Layout class called PlayerIcon. the cpp looks like this
void PlayerIcon::setPlayer(string userName, string displayName, string avatarUrl){
try {
//some code here
downloadAvatar(_userName, _avatarUrl);
//some code here
}
catch(... ){
}
}
void PlayerIcon::downloadAvatar(std::string _avatarFilePath,std::string url) {
if(!isFileExist(_avatarFilePath)) {
try {
auto downloader = new Downloader();
downloader->onFileTaskSuccess=CC_CALLBACK_1(PlayerIcon::on_download_success,this);
downloader->onTaskError=[&](const network::DownloadTask& task,int errorCode,
int errorCodeInternal,
const std::string& errorStr){
log("error while saving image");
};
downloader->createDownloadFileTask(url,_avatarFilePath,_avatarFilePath);
}
catch (exception e)
{
log("error while saving image: test");
}
} else {
//set texture for button
}
}
void PlayerIcon::on_download_success(const network::DownloadTask& task){
_isDownloading = false;
Director::getInstance()->getScheduler()-> performFunctionInCocosThread(CC_CALLBACK_0(PlayerIcon::reload_avatar,this));
}
void PlayerIcon::reload_avatar(){
try {
// setting texture in UI thread
}
catch (...) {
log("error updating avatar");
}
}
As i said, things works fine until PlayerIcon is deleted before the download is done.
i dont know what happens when the call back of the download task point to a method of un object that s deleted (or flagged for deletion).
i looked in the downloader implementation and it doesn't provide any cancellation mechanism
and i'm not sure how to handle this
Also, is it normal to have 10% crash rate on google console for a cocos2dx game
any help is really appreciated
Do you delete de Downloader in de destructor of the PlayerIcon?
there is a destroy in the apple implementation witch is trigered by the destructor.
-(void)doDestroy
{
// cancel all download task
NSEnumerator * enumeratorKey = [self.taskDict keyEnumerator];
for (NSURLSessionDownloadTask *task in enumeratorKey)
{
....
DownloaderApple::~DownloaderApple()
{
DeclareDownloaderImplVar;
[impl doDestroy];
DLLOG("Destruct DownloaderApple %p", this);
}
In the demo code of cocos2d-x: DownloaderTest.cpp they use:
std::unique_ptr<network::Downloader> downloader;
downloader.reset(new cocos2d::network::Downloader());
instead of:
auto downloader = new Downloader();
It looks like you are building this network code as part of your scene tree. If you do a replaceScene/popScene...() call, while the async network software is running in the background, this will cause the callback to disappear (the scene will be deleted from the scene-stack) and you will get a SEGFAULT from this.
If this is the way you've coded it, then you might want to extract the network code to a global object (singleton) where you queue the requests and then grab them off the internet saving the results in the global-object's output queue (or their name and location) and then let the scene code check to see if the avatar has been received yet by inquiring on the global-object and loading the avatar sprite at this point.
Note, this may be an intermittent problem which depends on the speed of your machine and the network so it may not be triggered consistently.
Another solution ...
Or you could just set your function pointers to nullptr in your PlayerIcon::~PlayerIcon() (destructor):
downloader->setOnFileTaskSuccess(nullptr);
downloader->setOnTaskProgress(nullptr);
Then there will be no attempt to call your callback functions and the SEGFAULT will be avoided (Hopefully).
Problem:
I have a private variable that is available during the startup of a threaded object, but is out of scope when it is used later (via a signal and slot call).
Details:
I have an application that I'm developing in Qt5 for both linux and windows.
Currently it works as expected under linux (where development began), but now
that I'm trying to stand it up on windows 7 (I didn't have a copy of windows initially) I have run into this problem where (on windows only) my private variables go out of scope after the thread initializes.
Question:
What is wrong with my object/thread structure such that the variable scope is fine under Linux, but not in windows? I thought that was the kind of "behind the scenes" stuff Qt took care of? (clearly not)
More Detail:
The order of operation goes like this
Instantiate an object
Move the object into a thread
Get the thread's start signal and call an init function in the object
Later, get data and emit a signal to the threaded object
Threaded object processes data
The code outlining the steps above is summarized below.
void MyWorkerClass::init()
{
// ... bunchOCode
procThread = new QThread; // <-- procThread - private to MyWorkerClass
procObj = new Processor(startupData); // <-- procObj - private to MyWorkerClass
procObj->moveToThread(procThread);
connect(procThread, SIGNAL(started()), procObj, SLOT(doStart()));
connect(this, SIGNAL(dataIsReady(void *)), procObj, SLOT(processMsgs(void *)));
procThread->start();
ok = waitforProcSetup();
// ... Life is good, do more stuff
}
class Processor : public QObject
{
// ... Other
// ... stuff
private slots:
void doStart();
void processMsgs(void * buffer);
private:
QHash<QString, bool> process;
}
void Processor::doStart() // <-- private slot
{
// ... take care of init stuff that couldn't be done in constructor
// Variable is valid here and I can work with it.
foreach(site, locations.uniqueKeys()) {
process[site] = true; // <-- works like a champ
qDebug() << QString("%1 => %2").arg(site).arg(process[site]);
}
}
void Processor::processMsgs(void * buffer) // <-- buffer is malloc'd memory and works fine
{
// ... When MyWorkerClass gets some data it emits a signal that is connected
// to this private slot.
// Simply trying to examine the variable causes a segfault (because it's uninitialized here)
qDebug() << "... processMsgs:" << process.isEmpty(); // <-- wets the bed
}
.
In trying to improve my question, by following the suggestions from the people who commented, I found out what was going on. I was working on creating a small working version of the example I posted (thanks Kuba Ober). The "error" I was encountering was a segmentation fault that could consistently be recreated with the debug line:
qDebug() << "... processMsgs:" << process.isEmpty(); // <-- wets the bed
Specifically, the private QHash variable "process" was useable when I called it the first time (after the thread was up and running)
connect(procThread, SIGNAL(started()), procObj, SLOT(doStart()));
but that same variable acted like it had gone out of scope when I tried to call it the second time
connect(this, SIGNAL(dataIsReady(void *)), procObj, SLOT(processMsgs(void *)));
The signal (dataIsReady(void *)) for this second call is an explicit "emit" that the worker class does when it's collected some data that can be processed. I tried to make that clear in the comments of the example pseudocode, but I didn't take into account that the comment code I included wouldn't stand out that well since it's all grey.
What was really going on was right after I filled "process[site]" with data I also looped over a quint64 array filling it with data too. The loop went 1 element too far and wrote into the "process" variable, making it look to me like it had gone out of scope. In linux it was purely coincidental that it didn't segfault (likely there was padding between the array and the QHash), but the windows runtime exposed the error for the first time.
I'm currently doing a FTP download using MFC. Is a very simple program which takes 2 inputs from user and click a download button in order to download from server. Everything is fine and im able to download it from. But i realized this program can only be executed once. Either successful or fail user has to open the .exe again to download another file. I'm a beginner in C&C++ with a simple knowledge i put OnInitDialog() at the last line of the download function hopping it will loop back and initialize again. Of course it doesn't work. Below are my current codes for the download button
BOOL CFTPDOWNLOADDlg::Log_In(char* path, char* ID, char* password {
m_pFtpConnection = NULL;
try{
// path
// ID
// password
m_pFtpConnection = m_Session.GetFtpConnection(path,
ID,password,INTERNET_INVALID_PORT_NUMBER);
}
catch(CInternetException *pEx){
pEx->ReportError(MB_ICONEXCLAMATION);
m_pFtpConnection = NULL;
pEx->Delete();
return FALSE;
}
return TRUE;
}
BOOL CFTPDOWNLOADDlg::Download(){
m_Edit3.SetWindowText("Downloading..");
m_Session.EnableStatusCallback(TRUE);
if(m_pFtpConnection->GetFile(serv_Loc,host_Loc,
FALSE,FILE_ATTRIBUTE_NORMAL,FTP_TRANSFER_TYPE_BINARY,1) != 0){
MessageBox("Download Complete");
m_Edit3.SetWindowText("");}
else{
MessageBox("Download Fail");
return FALSE;
}
// Log_out Session
m_Session.Close();
m_pFtpConnection->Close();
if(m_pFtpConnection!=NULL) delete m_pFtpConnection;
else MessageBox("Download Complete");
return TRUE;
}
BOOL CFTPDOWNLOADDlg::get_Path(){
...
...
...
sprintf(serv_Loc,"soft\\%s\\%d\\%s.zip",s_No,r_Number,r_No);
sprintf(host_Loc,"%s\\%s.zip",buff2,r_No);
return TRUE;
}
void CFTPDOWNLOADDlg::OnCancel() {
// Log_out Session
m_Session.Close();
m_pFtpConnection->Close();
if(m_pFtpConnection!=NULL)
delete m_pFtpConnection;
CDialog::OnCancel();
}
void CFTPDOWNLOADDlg::OnDLButton() {
//get path from user input
get_Path();
// start download
Download();
}
I've tried to search online, i couldn't find anything which is close. Sorry for my poor explanation.
Thank you in advance for your kindness in replying
Here is what you need to do:
You should make CInternetSession m_Session; a member of your CWinApp-derived class.
You should call m_Session.Close() in ExitInstance() method of your CWinApp-derived class.
In your CDialog-derived class you should only deal with CFtpConnection related stuff. So when user clicks on Download button you should call GetFtpConnection() and initialize your m_pFtpConnection and do the rest. When download/upload is done call m_pFtpConnection->Close(); and delete m_pFtpConnection;
Please also use CString instead of char*. There are lots of benefits like automatic UNICODE support, etc.
Please also consider using CString::Format() method instead of sprintf().
You should also consider using threads to perform upload/download tasks in a separate worker thread. Use AfxBeginThread() to start the thread. This way you'll not affect Windows message pump that is a part of main application (GUI) thread. So your GUI wont lock up while you uploading/downloading files.
I am using timers with resource ID added and based on WM_TIMER message.
I would like to call a routine like DrunkenDragon() on OnTimer() but for only once after SetTimer(id,10sec,NULL) was called. We know that Call to KillTimer() inside DrunkenDragon() routine would fix the solution. Is it okay to go with this, or am I missing out something great with timers.
(Only answering this in case someone else comes across it like I did and was unsatisfied with the answers available)
So, in WindowClass.h, what you can do is an enumeration of the timer identifiers you want to use. While you certainly can use raw numeric values, using symbols is probably easier to work with in the long run.
class WindowClass : CWnd
{
// other parts of the interface...
protected:
enum
{
TIMER_MAIN_UPDATE = 1,
TIMER_PLASTERED_DRAGON
};
};
Meanwhile, back at in WindowClass.cpp,
int WindowClass::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// { ... other initialization code }
// In case you want to do other types of updates at regular intervals.
SetTimer(TIMER_MAIN_UPDATE, 1000, NULL);
// Note the symbolic identifiers.
SetTimer(TIMER_PLASTERED_DRAGON, 10000, NULL);
return 0;
}
That's only any good if you want to do it 10 seconds after the window's been created, though. You can also just call SetTimer() in some other event handler whenever you'd like:
void WindowClass::OnJustGotPaid()
{
// { ... other handling }
// Since our dragon is a real lightweight, it apparently only takes
// 10 seconds to get him puking up flaming vomit.
SetTimer(TIMER_PLASTERED_DRAGON, 10000, NULL);
}
When it comes time for the actual event to be handled, it is typically handled in the Windows OnTimer() callback. A timer event can be directed to a different (custom) callback, if desired, by specifying a valid function pointer in SetTimer()'s third parameter instead of NULL.
void WindowClass::OnTimer(UINT_PTR p_timer_id)
{
switch(p_timer_id)
{
default:
break;
case TIMER_MAIN_UPDATE:
// { ... main update code }
break;
case TIMER_PLASTERED_DRAGON:
// Killing the timer first in case DrunkenDragon() takes a good
// long while for whatever reason.
KillTimer(TIMER_PLASTERED_DRAGON);
DrunkenDragon();
break;
}
}
int CYourDialog::OnInitDialog()
{
__super::OnInitDialog();
SetTimer(0x10, 10000, NULL);
return true;
}
void CYourDialog::OnTimer(UINT_PTR ignore)
{
DrunkenDragon();
}
And ensure you have ON_WM_TIMER in message-map.
You are not missing anything and you would have to use KillTimer for system to stop generating WM_TIMER messages.
You can also use CreateTimerQueueTimer and set parameters the way a callback is called only once.
See this for more details.