文章目录
- my ttl clock
- 概述
- 实现
- 补充
- 补充
- END
my ttl clock
概述
最近想检测一下自己的睡眠质量.
想做个闹钟, 从22:00~03:00, 每隔5分钟报时. 将小时:分钟念出来就行. 这样, 我入静之后, 听到的最后时间, 就是我入睡的时间.
看到网上同学写好一个ttl类, 改了一下. 加了检测声音播放完的函数.
加入了任务时间段跨零点的判断.
任务结束时, 将本本关机.
花了1天时间, 手搓了一个工具, 完成了这个功能.
前几年, 买华为手机时, 附赠了一个AM08的蓝牙音箱, 一直没用过, 试了试, 还和新的一样. 这次正好用上.
在卧室摆上蓝牙音箱, 调节好音量, 在本本上开这个ttl clock程序, 准备睡觉时, 将音频切到蓝牙音箱上.
平时, 将蓝牙音箱断开, 用本本的声卡发声.
实现
写好的程序如下:
// my_ttl_clock.cpp
// env = vs2022 c++ console win10-22H2
// 需要禁止警告 4996#include "pch.h"
#include "ttl.h"// 任务时间从开始到结束
#define TM_TASK_BEGIN_HOUR 22
#define TM_TASK_BEGIN_MINU 00 // minute#define TM_TASK_END_HOUR 3
#define TM_TASK_END_MINU 0#define TM_ALERT_SPAN 5 // 多少分钟告警一次, 值范围 (1 ~ 59)bool get_cur_time(int* year, int* month, int* day, int* hour, int* minute, int* second);
void wait_for_next_minute(int _minute);
bool is_in_task_time(int year, int month, int day, int hour, int minute, int second);int main()
{int i = 0;int year = 0;int month = 0;int day = 0;int hour = 0;int minute = 0;int second = 0;int entry_cnt = 0;bool b_work_once = false;TTS ttl;TCHAR szBuf[0x100];do {Sleep(1000);if (!get_cur_time(&year, &month, &day, &hour, &minute, &second)){break;}_stprintf(szBuf, _T("%d点%.2d"), hour, minute);_tprintf(_T("now time : %2.2d:%2.2d:%2.2d\n"), hour, minute, second);if (entry_cnt++ < 3){// 程序启动后的头3次每秒, 需要报警, 用于测试音频播放是否正常_tprintf(_T("test sound!\n"));ttl.Speak_until_over(szBuf);}if ((21 == hour) && (minute == 30)){// 21:30 准备进屋-报警 PM的需求_tprintf(_T("PM need this!\n"));for (i = 0; i < 6; i++){ttl.Speak_until_over(szBuf);ttl.Speak_until_over(_T("准备进屋")); // testSleep(100);}}if (is_in_task_time(year, month, day, hour, minute, second)){// 我的需求 22:00 ~ 03:00, 每隔5分钟, 报警一次// workif (0 == (minute % TM_ALERT_SPAN)) {_tprintf(_T("alert!\n"));b_work_once = true;// workttl.Speak_until_over(szBuf);}}else {// 过了3点, 关机, 任务完成了if (b_work_once){// 如果进入过我的任务, 就关机了// 需要管理员权限// ExitWindowsEx(EWX_POWEROFF | EWX_FORCEIFHUNG, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_MAINTENANCE);system("shutdown /s");break;}}wait_for_next_minute(minute);} while (true);Sleep(1000);_tprintf_s(_T("END\n"));system("pause");
}bool is_in_task_time(int year, int month, int day, int hour, int minute, int second)
{/*
#define TM_TASK_BEGIN_HOUR 22
#define TM_TASK_BEGIN_MINU 00 // minute#define TM_TASK_END_HOUR 3
#define TM_TASK_END_MINU 0
*/int i_begin_hour = TM_TASK_BEGIN_HOUR;int i_begin_minute = TM_TASK_BEGIN_MINU;int i_end_hour = TM_TASK_END_HOUR;int i_end_minute = TM_TASK_END_MINU;int i_end_hour_org = i_end_hour;long l_tick_begin = 0;long l_tick_end = 0;long l_tick_cur = 0;if (i_begin_hour < 0) { i_begin_hour = 0; }else if (i_begin_hour > 23) { i_begin_hour = 23; }if (i_begin_minute < 0) { i_begin_minute = 0; }else if (i_begin_minute > 59) { i_begin_minute = 59; }if (i_end_hour < 0) { i_end_hour = 0; }else if (i_end_hour > 23) { i_end_hour = 23; }if (i_end_minute < 0) { i_end_minute = 0; }else if (i_end_minute > 59) { i_end_minute = 59; }if (i_end_hour < i_begin_hour){// 跨零点i_end_hour += 24;if (hour < i_end_hour_org){hour += 24;}}l_tick_begin = i_begin_hour * 60 + i_begin_minute;l_tick_end = i_end_hour * 60 + i_end_minute;l_tick_cur = hour * 60 + minute;return ((l_tick_cur >= l_tick_begin) && (l_tick_cur <= l_tick_end));
}void wait_for_next_minute(int _minute)
{int year = 0;int month = 0;int day = 0;int hour = 0;int minute = 0;int second = 0;_tprintf(_T("wait for next minute...\n"));do {if (!get_cur_time(&year, &month, &day, &hour, &minute, &second)){break;}if (minute != _minute){break; // next minute arrive}Sleep(100);} while (true);
}bool get_cur_time(int* year, int* month, int* day, int* hour, int* minute, int* second)
{time_t rawtime;struct tm* timeinfo = NULL;time(&rawtime);timeinfo = localtime(&rawtime);if (NULL == timeinfo){return false;}// Tue Apr 11 21:04:31 2023// printf("The current date/time is: %s", asctime(timeinfo));/* tm_sec 54 inttm_min 6 inttm_hour 21 inttm_mday 11 inttm_mon 3 int + 1tm_year 123 int + 1900tm_wday 2 inttm_yday 100 inttm_isdst 0 int
*/if (NULL != year){*year = timeinfo->tm_year + 1900;}if (NULL != month){*month = timeinfo->tm_mon + 1;}if (NULL != day){*day = timeinfo->tm_mday;}if (NULL != hour){*hour = timeinfo->tm_hour;}if (NULL != minute){*minute = timeinfo->tm_min;}if (NULL != second){*second = timeinfo->tm_sec;}return true;
}
#pragma once#include "pch.h"
#include "sapi.h"#include "sphelper.h"
#pragma comment(lib, "sapi.lib")class TTS
{
public:TTS();~TTS();bool Speak(std::wstring);bool IsUseful();bool Speak_until_over(std::wstring);DWORD get_status();private:ISpVoice* m_pSpVoice;bool is_play_over();bool InitVoice();void StopVoice();
};
#include "pch.h"
#include "ttl.h"TTS::TTS()
{m_pSpVoice = nullptr;
}TTS::~TTS()
{StopVoice();::CoUninitialize();
}bool TTS::InitVoice()
{HRESULT ret = ::CoInitialize(NULL);ret = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (void**)&m_pSpVoice);if (0 == ret && m_pSpVoice){m_pSpVoice->SetVolume(80);IEnumSpObjectTokens* pSpEnumTokens = NULL;if (SUCCEEDED(SpEnumTokens(SPCAT_VOICES, NULL, NULL, &pSpEnumTokens))) //找到lili语音进行朗读, 否则就跳出{ISpObjectToken* pSpToken = NULL;while (SUCCEEDED(pSpEnumTokens->Next(1, &pSpToken, NULL)) && pSpToken != NULL){LPWSTR pId = new WCHAR[32];pSpToken->GetId(&pId);int nLen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pId, -1, NULL, 0, NULL, NULL);char* s = new char[nLen + 1];WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pId, -1, s, nLen, NULL, NULL);if (0 == strcmp(s, ("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices\\Tokens\\MS-Lili-2052-20-DSK"))){m_pSpVoice->SetVoice(pSpToken); // 设置lili语音pSpToken->Release();delete[] s;break;}delete[] s;pSpToken->Release();}pSpEnumTokens->Release();}return true;}else{}return false;
}bool TTS::Speak(std::wstring word)
{DWORD dwRc = -1;if (InitVoice()){HRESULT ret = m_pSpVoice->Speak(word.c_str(), SPF_ASYNC /*SPF_ASYNC*/, NULL);if (S_OK != ret){return false;}do {dwRc = get_status();if ((0 != dwRc) && (1 != dwRc) && (2 != dwRc) && (3 != dwRc)){break;}if (0 == dwRc){// playingbreak;}} while (true);return (S_OK == ret);}else{return false;}}DWORD TTS::get_status()
{DWORD dw_rc = -1; // 默认无效状态HRESULT hr = S_OK;SPVOICESTATUS status;wchar_t szBuf[0x100];LPWSTR p = szBuf;do {if (NULL == m_pSpVoice){break;}memset(szBuf, 0, sizeof(szBuf));memset(&status, 0, sizeof(status));hr = m_pSpVoice->GetStatus(&status, &p);if (S_OK != hr){break;}dw_rc = status.dwRunningState;} while (false);return dw_rc;
}bool TTS::Speak_until_over(std::wstring str)
{do {if (is_play_over()){// StopVoice();break;}} while (true);if (!Speak(str)){return false;}do {if (is_play_over()){// StopVoice();break;}} while (true);return true;
}bool TTS::is_play_over()
{bool b_play_over = true; // 默认为播放完成状态DWORD dwRc = -1;do {if (NULL == m_pSpVoice){break;}dwRc = get_status();if ((0 != dwRc) && (1 != dwRc) && (2 != dwRc) && (3 != dwRc)){break;}if (1 == dwRc){break; // play over}} while (true);return b_play_over;
}bool TTS::IsUseful()
{bool bRet = InitVoice();StopVoice();return bRet;
}void TTS::StopVoice()
{if (m_pSpVoice != nullptr){m_pSpVoice->Release();}
}
补充
将程序的快捷方式丢进当前用户开机启动文件夹(C:\Users\me\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup), 计算机早上打开, 这个程序就运行, 到了夜里3点, 这个程序就将计算机关掉.
睡觉前, 将音频切到蓝牙音箱上, 将计算机音量开到30%. 在夜深人静的时候, 听到的蓝牙音箱发出的声音不至于惊到人.
补充
发现有几天, 早上起来, 计算机没有关机, 但是按任意键只是亮灯, 没动作. 只能强行电源键长按关机.
昨天, 将电源改为10分钟关闭屏幕, 永远不睡眠. 将多余的程序都关掉, 只剩下杀毒软件和ttl clock.
早上起来, 计算机已经很好的关机了.