ZOOM, Teamsなどのためのサブタイトル(字幕)表示ツール
ZOOM, Teamsなどのためのサブタイトル(字幕)表示ツール
ZOOM, Teamsなどのリモート会議システムで手軽にサブタイトル(字幕)を表示するアプリケーションを書いてみる。
送信パートと受信(表示)パートを分けて作成する。(ひとつにするとスレッドとかめんどくさいので。)
送信パートは以下のようにVC++だけで書いた短いプログラム。
エディットボックスの中に漢字を書いて送信ボタンをプレスするとudpで漢字が送出される。
(buildが苦手という方は、このhttps://github.com/ultrahamlet/SjFX
gitにあるbinaryファイルで実験できる。
SjFX.exeが受信・表示プログラム。WindowsProject.exeが文字送信プログラム。
実験時にWebCamの接続を忘れずに。)
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "wsock32.lib")
#include <winsock2.h>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <iostream>
#include <comdef.h>
#define MSG(m) {\
MessageBoxA(NULL,m,NULL,MB_OK);}
//ウィンドウハンドル
HWND hwnd;
//インスタンスハンドル
HINSTANCE hinst;
//ウィンドウ横幅
#define WIDTH 500
#define HEIGHT 300
//ボタンウィンドウハンドル
HWND hwnd_child;
HWND hwnd_child2;
#define CHILD_ID 1
#define BUTTON_ID1 2
// socket
SOCKET sock;
// アドレス等格納
struct sockaddr_in addr;
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_COMMAND:
if (LOWORD(wp) == CHILD_ID) {
switch (HIWORD(wp)) {
case EN_SETFOCUS:
// set focus
return 0;
case EN_KILLFOCUS:
// kill focus
return 0;
case EN_UPDATE:
//
return 0;
case EN_CHANGE:
return 0;
}
}
if (LOWORD(wp) == BUTTON_ID1) {
LPWSTR str = new WCHAR[256 + 1];
GetWindowText(hwnd_child, str, 256);
SetWindowText(hwnd, str);
//char buf[512];
// バッファ char配列
_bstr_t b(str);
const char *buf = b;
char sbuf[512];
for (int i = 0; i < b.length() * 2; i++) {
sbuf[i] = buf[i];
}
//sbuf[b.length() * 2] = 0;
// 送信
//sendto(sock, sbuf, sizeof(buf)* b.length()/2, 0, (struct sockaddr *)&addr, sizeof(addr));
sendto(sock, sbuf, sizeof(sbuf), 0, (struct sockaddr *)&addr, sizeof(addr));
free(str);
return 0;
}
break;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MSG msg;
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hCursor = wc.hIcon = NULL;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszClassName = _T("test");
wc.lpszMenuName = NULL;
if (!RegisterClass(&wc)) {
MSG("クラスの登録失敗");
return -1;
}
// メインウィンドウ
hwnd = CreateWindowA("test", "test", WS_VISIBLE | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX,
0, 0, WIDTH, HEIGHT, NULL, NULL, hinst, NULL);
//EDITコントロール作成
hwnd_child = CreateWindowA("edit", NULL, WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_LEFT,
20, 20, 400, 100, hwnd, (HMENU)CHILD_ID, hInstance, NULL);
//ボタン作成
hwnd_child2 = CreateWindow(
TEXT("BUTTON"), TEXT("SEND"),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
240, 150, 100, 50,
hwnd, (HMENU)BUTTON_ID1, hInstance, NULL
);
if (hwnd == NULL || hwnd_child == NULL || hwnd_child2 == NULL) {
MSG("ウィンドウ作成失敗");
return -1;
}
//
hinst = hInstance;
//
WSAData wsaData;
WSAStartup(MAKEWORD(2, 0), &wsaData); //
// socket作成
sock = socket(AF_INET, SOCK_DGRAM, 0); //
addr.sin_family = AF_INET; //IPv4
addr.sin_port = htons(12345); //通信ポート
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 送信アドレス
int check;
while (check = GetMessage(&msg, NULL, 0, 0)) {
if (check == -1) {
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//クラス解放
UnregisterClass(_T("test"), hinst);
// socketの破棄
closesocket(sock);
// winsockの終了
WSACleanup();
return 0;
}
次は、これを受信するテストプログラム
openframeworksで書いた。
漢字を受信して表示したところ。「漢字のテストです。」の行が受信して表示されたところ。
先ずは、ofApp.h
ofApp.h
#pragma once
#include "ofMain.h"
#include <Windows.h>
#include "ofxNetwork.h"
//#include "ofxTrueTypeFontUC.h"
//#include "ofxGui.h"
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void keyPressed(int key);
void keyReleased(int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void mouseEntered(int x, int y);
void mouseExited(int x, int y);
void windowResized(int w, int h);
void dragEvent(ofDragInfo dragInfo);
void gotMessage(ofMessage msg);
void onTextChange(std::string & text);
private:
ofTrueTypeFont font;
string str;
string str2;
int keyIndex;
//ofxPanel _gui;
ofParameterGroup _parameters;
ofParameter<std::string> _textParameter;
ofEventListener _textParameterListener;
// udp
ofxUDPManager udpConnectionRx;
ofxUDPManager udpConnectionTx;
string rxMessage;
string txMessage;
string recvStr;
string ostr;
BOOL received;
};
続いて、ofApp.cpp
フォントはWindows/Fontsからコピペする。
漢字をきちんと表示するところによっと手間取った。
ofApp.cpp
#include "ofApp.h"
#include <codecvt>
vector<string> split(const string &s, char delim) {
vector<string> elems;
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
if (!item.empty()) {
elems.push_back(item);
}
}
return elems;
}
std::wstring multi_to_wide_capi(std::string const& src)
{
std::size_t converted{};
std::vector<wchar_t> dest(src.size(), L'\0');
if (::_mbstowcs_s_l(&converted, dest.data(), dest.size(), src.data(), _TRUNCATE, ::_create_locale(LC_ALL, "jpn")) != 0) {
throw std::system_error{ errno, std::system_category() };
}
dest.resize(std::char_traits<wchar_t>::length(dest.data()));
dest.shrink_to_fit();
return std::wstring(dest.begin(), dest.end());
}
std::string wide_to_utf8_cppapi(std::wstring const& src)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(src);
}
std::string multi_to_utf8_cppapi(std::string const& src)
{
auto const wide = multi_to_wide_capi(src);
return wide_to_utf8_cppapi(wide);
}
std::string toUtf8(const std::wstring &str)
{
std::string ret;
int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0, NULL, NULL);
if (len > 0)
{
ret.resize(len);
WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len, NULL, NULL);
}
return ret;
}
//--------------------------------------------------------------
void ofApp::setup(){
ofTrueTypeFontSettings settings("jadhei01m.ttf", 24);
settings.addRanges(ofAlphabet::Emoji);//絵文字
settings.addRanges(ofAlphabet::Japanese);//日本語
settings.addRange(ofUnicode::Space);//スペース
settings.addRange(ofUnicode::IdeographicSpace);//全角スペース
settings.addRange(ofUnicode::Latin);//アルファベット等
settings.addRange(ofUnicode::Latin1Supplement);//記号、アクサン付き文字など
settings.addRange(ofUnicode::NumberForms);//数字?
settings.addRange(ofUnicode::Arrows);//矢印
settings.addRange(ofUnicode::MathOperators);//数式記号
settings.addRange(ofUnicode::Hiragana);//ひらがな
settings.addRange(ofUnicode::Katakana);//カタカナ
settings.addRange(ofUnicode::MiscSymbolsAndPictographs);//絵文字など
settings.addRange(ofUnicode::Emoticons);//エモーティコン
//
if (!font.load(settings))
cout << "couldn't load font" << endl;
auto bufferLines = ofBufferFromFile("intro.txt").getText();
for (int i = 0; i < bufferLines.size(); i++) {
str += bufferLines[i];
}
font.setLetterSpacing(1.2);
keyIndex = 0;
//std::cin >> str2;
_textParameter.addListener(this, &ofApp::onTextChange);
_parameters.setName("params");
_parameters.add(_textParameter.set("text", "default"));
//_gui.setup(_parameters);
udpConnectionRx.Create();
udpConnectionRx.Bind(12345); //incomming data on my port # ...
udpConnectionRx.SetNonBlocking(true);
received = false;
}
//--------------------------------------------------------------
void ofApp::update(){
char data[512];
udpConnectionRx.Receive(data, 512);
uint16_t *val = (uint16_t*)data;
//std::cout << val[0] << std::endl;
BOOL strOK = true;
for (int i = 0; i < sizeof(val); i++) {
if (val[i] < 256) { strOK = false; break; }
}
if (strOK) {
recvStr = data;
received = true;
std::cout << recvStr << std::endl;
// shift-jis -> utf8
ostr = multi_to_utf8_cppapi(recvStr);
}
}
//--------------------------------------------------------------
void ofApp::draw(){
ofClear(0);
ofSetColor(255, 255, 255);
font.drawString(u8"漢字テスト", 10, 200);
vector<string> text = split(str, '\n');
font.drawString(text[keyIndex], 20, 370);
if(received)font.drawString(ostr, 10, 250);
}
//--------------------------------------------------------------
今回はこれまで、これをZOOMやTeamsの仮想カメラとして認識する完成版は次の投稿で...
その2はこちら、https://qiita.com/quittardis/items/17cdb78892aa1c1013a1
Author And Source
この問題について(ZOOM, Teamsなどのためのサブタイトル(字幕)表示ツール), 我々は、より多くの情報をここで見つけました https://qiita.com/quittardis/items/317df7b23a64e82bf6a2著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .