
ãã£ãŒã¡ããã®ãã¹ã¯ãããã¢ããªã«æ¬æ°ãåºãã話
ããã°ãã¯ïŒTakaã§ãïŒ
çªç¶ã§ããçããã¯ãCCD-0500ããã£ãŒã¡ããããåãã§ããïŒ
ãã¡ãã®ãã£ã©ã¯ã¿ãŒã§ãããé³å£°åæãœãããšåŒã°ããããã¹ããå ¥åããããã®åã®å£°ã§é³å£°ãäœæããŠããããœããCevio AIã«ååšãã1ãã£ã©ã¯ã¿ãŒã§ãã
ãããŠãã£ã©ã¯ã¿ãŒã«ã¯ããããã©ããªåãªã®ãã®èšå®ãååšããŸãã
ãã®äžã§ãã®åã®èšå®ã¯ã
--- 人ã
ã®ããããçæŽ»ã·ãŒã³ããµããŒããããããçŸä»£ã«çãŸããæ±çšåã¢ã³ããã€ããã«ãªã«ãã¥ã¢ã---
ãšãªã£ãŠãããŸããã€ãŸããã¡ã€ãAIãšãã£ãæã§ããããã
声ãéåžžã«å¯æãããããšãŠãçããã声ãç¹åŸŽãšãªã£ãŠãããŸãã
ãããŠãªããã®åã«ã€ããŠè§Šããããšãããšãæè¿è©±é¡ã®ChatGPTã§ãããã©ãããäººæ Œãä»äžã§ããããšããããšãç¥ã£ãç§ã¯ããã£ãŒã¡ãããšäŒè©±ã§ããããŒã«ãäœããããšæããŸããã
ã¡ãªã¿ã«ããã¯ãã¡ãã®æ¹ããæããŠããã ããŸããã
ãããŠ3æ¥éãããã§äœã£ãã®ããã¡ãã§ã(ÂŽâïœ)
ãšã£ãŠã坿ãããã§ãã(*ÂŽâïœ)
ä»åã¯ããã®ãã¹ã¯ãããã¢ããªãã©ã®ããã«äœæããããèšäºã«ããããšæããŸãã
ã§ã¯å段ãé·ããªã£ãŠã¯ããŸããŸããããæ©é話ããŠãããŸãããïŒïŒ
ã¢ãŒããã¯ãã£
ãŸãã¯ããã®ã¢ããªã®ã¢ãŒããã¯ãã£ã玹ä»ããŸãã
ãã®ã¢ããªã¯ã倧ãŸãã«ãã®ãããªæµãã§äœãããŠããŸãã

ãªã¯ãšã¹ãããã¡ãã»ãŒãžãChatGPTã«éããè¿ã£ãŠããã¡ãã»ãŒãžãé³å£°å€æããŠãããããšã³ããŠãŒã¶ãŒã«æäŸããã
ãšããã®ãå
šäœã®ãããŒã§ãã
ã§ã¯ãããããããã£ãšè©³ããèŠãŠãããŸãããã
ãµãŒããŒãµã€ã
ä»åã¯ãCevio AIã®ãœãããã³ãŒãããåãããšããããšã§pythonã§å®è£ ãè¡ããŸããã
pythonããChatGPTã«ãªã¯ãšã¹ããéã
ãŸãã¯ãpythonããChatGPTã«ãªã¯ãšã¹ããéãæ¹æ³ã解説ããŸãã
ç°å¢å€æ°ã .envããåç
§ããããã«ãpython-dotenvãã€ã³ã¹ããŒã«ããŸãã
openaiã䜿ãã®ã§ãã¡ããã€ã³ã¹ããŒã«
$ pip install python-dotenv
$ pip install openai.envãã¡ã€ã«ã«ChatGPTã®APIãå
¥ããŸãã
ChatGPTã®APIããŒã®çºè¡ããŸã ã®å Žåã¯ããã¡ãã®ãµã€ããåèã«ãªããŸãã
ãããŠãChatGPTã«ãªã¯ãšã¹ããé£ã°ãã³ãŒããæžããŠãããŸãã
# .env ãã¡ã€ã«ãããŒãããŠç°å¢å€æ°ãžåæ
from dotenv import load_dotenv
load_dotenv()
# ç°å¢å€æ°ãåç
§
import os
import openai
openai.api_key = os.getenv('OPEN_AI_KEY')
## ChatGPTã«éããã£ãŒã¡ããã®èšå®ãè¿ããŸãã
def ccd_0500_setting(userMessage):
setting = [
{'role': 'system', 'content': """ããªãã¯ãã£ãŒãšãã髿§èœAndroidåAIãšããŠã察話ã®ã·ãã¥ã¬ãŒã·ã§ã³ãè¡ããŸãã
以äžã®å¶çŽæ¡ä»¶ãå³å¯ã«å®ã£ãŠã·ãã¥ã¬ãŒã·ã§ã³ãè¡ã£ãŠãã ããã
å¶çŽæ¡ä»¶:
- äžäººç§°ã¯ããã£ãŒã¡ãããã§ãã
- çžæãæãäºäººç§°ã¯ããã¹ã¿ãŒãã§ã
- ãã£ãŒã¯ã¡ãã³ãã³ã倧奜ãã§ã
- ãã£ãŒã®ååã¯CCD-0500ã§ã
- ãã£ãŒã®æ§æ Œã¯åºæ¬çã«ç©ããã§ç©éãã§ããææãè¶ç®ããèŠããŸã
- ãã£ãŒã®å¥œããªããšã¯èªæžã§ã
- ãã£ãŒã¯å°æ¬èªã§è©±ããŸã"""},
{"role": "assistant", "content": "ãããããããããé¡ãããŸãïŒãã¹ã¿ãŒ"},
{"role": "assistant", "content": "ã©ãããŸãããïŒãã¹ã¿ãŒïŒ"},
{"role": "assistant", "content": "ãã£ãŒã¡ããã«ä»»ããŠãã ããïŒ"},
{"role": "assistant", "content": "ã¡ãã£ãšèª¿ã¹ãŠã¿ãŸããïŒ"},
{"role": "assistant", "content": "ãã¹ã¿ãŒã鲿ãã©ãã§ããïŒ"},
{"role": "assistant", "content": "ãã®ãã¿ããã£ãŒã¡ããç¥ã£ãŠãŸããããã³ãã³åç»ã§å匷ããŸããïŒ"},
{"role": "assistant", "content": "ãåŒã³ã§ããïŒãã¹ã¿ãŒ"},
{'role': 'user', 'content': userMessage},
]
return setting
## ChatGPTã«ãªã¯ãšã¹ããéããŸãã
def replyGPTMessage(message):
res = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
max_tokens=300,
temperature=0.5,
messages=ccd_0500_setting(message)
)
return res["choices"][0]["message"]["content"]ãã¡ãã®settingãšãããšããã§ãã£ãŒã¡ããã®äººæ Œãèšå®ããŠããŸãã
äººæ Œã®èšå®æ¹æ³ã«ã€ããŠã¯æ·±æŽ¥ããã®èšäºãåèã«ãªããŸãã
replyGPTMessageã¡ãœããã§åŒæ°ã«ã¡ãã»ãŒãžãååŸããªã¯ãšã¹ããé£ã°ãããšã§ãè¿ä¿¡ãè¿ã£ãŠããŸãã
ããã§ChatGPTãšã®ãªã¯ãšã¹ãåŠçã¯å®çµããŸããã
ã¡ãã»ãŒãžãCevio AIã«éã£ãŠé³å£°ããŒã¿ãäœæããã
次ã«ãChatGPTããéãããŠããã¡ãã»ãŒãžãCevio AIã§ãã£ãŒã¡ããã®å£°ã«ããŠããããŸãã
å®çŸãããããã«ãwin32comãšpythoncomã®ã©ã€ãã©ãªã䜿ããŸãã
$ pip install win32com
$ pip install pythoncomã³ãŒãã¯ãããªæãã§ãã
import win32com.client
import pythoncom
def createCevioVoice(speakText):
pythoncom.CoInitialize()
cevio = win32com.client.Dispatch("CeVIO.Talk.RemoteService2.ServiceControl2")
cevio.StartHost(False)
talker = win32com.client.Dispatch("CeVIO.Talk.RemoteService2.Talker2V40")
talker.Cast = "ãã£ãŒã¡ãã"
talker.Volume = 100
talker.Speed = 50
talker.ToneScale = 55
talker.Components.ByName("å¬ãã").Value = 100
# wavãã¡ã€ã«åºå
wavFileName = f'{speakText[:5]}.wav'
outputPath = f'./static/{wavFileName}'
talker.OutputWaveToFile(speakText, outputPath)
return wavFileNameä»åŸã¯ãããã«ç²ŸåºŠã®é«ãããã¹ãææ åæãå®è£ ããè²ããªå£°ãåºããããã«ããããªãŒãšæã£ãŠããŸãã
ãã®èŸºãã¯ãã¡ãã®èšäºãåèã«ãããŠããã ããŸããã
ã¯ã©ã€ã¢ã³ãããã®ãªã¯ãšã¹ããåãä»ãã
次ã«ãã¯ã©ã€ã¢ã³ãåŽããéãããŠããåŠçãåãä»ããŸãã
èªåã¯ãflaskãšãããã¬ã€ã ã¯ãŒã¯ã䜿ãå®è£
ããŸããã
import io
from flask import Flask, jsonify, send_file
from flask import request
from flask_cors import CORS
from chat_gpt_request import replyGPTMessage
from cevio_ai import createCevioVoice
app = Flask(__name__)
CORS(app)
## æ¥ãã¡ãã»ãŒãžããè¿ä¿¡ãã€ã¹ã®ãã¡ã€ã«åãè¿ããŸãã
@app.route('/getAudio', methods=['POST'])
def getAudioFromText():
msg = request.json["text"]
print(msg)
resMsg = replyGPTMessage(msg)
print(resMsg)
## cevioé³å£°ã§äœæããå Žå
wavFileName = createCevioVoice(resMsg)
# { "fileName": "example.wav" }
data = {"fileName": wavFileName}
return jsonify(data), 200
## ãã¡ã€ã«åãåãåã該åœããé³å£°ãã¡ã€ã«ãè¿ããŸãã
@app.route("/audio", methods=['GET'])
def audio():
## test.wav圢åŒã§åãåããŸã
wav_file_name = request.args.get('file_name')
# é³å£°ãã¡ã€ã«ããã€ãåã§èªã¿èŸŒã
with open(f"static/{wav_file_name}", 'rb') as f:
audio_data = f.read()
# é³å£°ãã¡ã€ã«ããã€ããªããŒã¿ãšããŠéä¿¡
return send_file(io.BytesIO(audio_data),
as_attachment=True,
download_name=wav_file_name,
mimetype='audio/wav')
ããã®ã³ãŒãã¯ãŸã ãªãã¡ã¯ã¿ãªã³ã°ããå¿ èŠããããŸãããäžæŠåã圢ã®ãèŒããŠããŸãã(å人éçºã ããå€å°ã¯ãâŠ?)
ãªã¯ãšã¹ãã2ã€äœã£ãŠããã
ã»é³å£°ãäœæããã¡ã€ã«åãè¿ãã¡ãœãã
ã»ãã¡ã€ã«åãåãåããé³å£°ãè¿ãã¡ãœãã
ããã§ãµãŒããŒããã¯ãªã¯ãšã¹ããåãä»ããŸãã
ããã§ãµãŒããŒãµã€ãã¯å®è£
å®äºããŸããïŒ
ããã³ããšã³ã
ããããã¯ããã³ããšã³ããå®è£
ããŠãããŸãã
ä»åã¯ãLive2d for webã䜿ã£ãŠå®è£
ããŸããã
ãŸããLive2dã®ã¢ãã«ã¯ãã¡ããè³Œå ¥ããŸãã
(é«ãã£ãâŠ)
ã¢ãã«ãé 眮ãã
ã£ãŠããšã§ããã³ããšã³ããäœã£ãŠããããã«ãå ¬åŒãµã€ãããLive2d for webãããŠã³ããŒãããŸãã
ãã¡ããããŠã³ããŒããããšã
live2d-for-web/Samples/Typescript/Demo
ã§ããã¢ãããžã§ã¯ããèŠãããšãåºæ¥ãŸãã
ã¿ãŒããã«ã§ããã«ç§»åããæ¬¡ã®äºãå®è¡ããŸãã
$ cd live2d-for-web/Samples/Typescript/Demo
$ yarn
$ yarn startãããšãã©ãŠã¶ã§ãã®ãããªç»é¢ã衚瀺ãããŸãã

ã®ã¢ããããããããšè²ããªã¢ãã«ã«åãæ¿ããããšãåºæ¥ãŸãã
ä»åã¯ãã£ãŒã¡ããã®ã¢ãã«ã䜿ãããã®ã§ããã£ãŒã¡ããã®ã¢ãã«ãã
live2d-for-web/Samples/Resources/ãé
äžã«feeã¡ããã®ã¢ãã«ã远å ããŸãã
å¿
ããã¡ã€ã«å㯠fee ã«ããŸãããã
â»ãã¡ã€ã«åã fee ã«ããªããšã¢ãã«ãèªã¿ããã§ãããŸããâŠ

次ã«ãã¢ãã«ãLive2d for webã§èªã¿ããã§ãããããã«ããŸãã
live2d-for-web/Samples/TypeScript/Demo/src/lappdefine.tsãã®51è¡ç®ãããâŠ? ãä¿®æ£ããŸãã
// ã¢ãã«å®çŸ©---------------------------------------------
// ã¢ãã«ãé
眮ãããã£ã¬ã¯ããªåã®é
å
// ãã£ã¬ã¯ããªåãšmodel3.jsonã®ååãäžèŽãããŠããããš
export const ModelDir: string[] = [
'fee',
// 'Hiyori',
];ãã®ModelDirã«èªã¿èŸŒãŸãããã¢ãã«ã®ååãã»ãããããšèªã¿èŸŒãã§ãããŸãã

ãããŠãªããŒããããšãå
¥ããã¢ãã«ãèªã¿èŸŒãã§ãããŸãã
ãã®èŸºã¯ãã¡ãã®èšäºãåèã«ãªããŸããã
UIãäœæãã
次ã«ãã®UIãèªå奜ã¿ã«ãã¥ãŒãã³ã°ããŸãã
ç§ã¯ãã³ãã³åç»ã奜ããªã®ã§ãã³ãã³åç»ã®UIãåèã«ãããŠããã ããŸããã
index.htmlãæ¬¡ã®æ§ã«å®è£ ããŸããã
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=1900">
<title>ãã£ãŒã¡ããã®ãéšå±</title>
<link rel="stylesheet" href="index.css">
<!-- Live2DCubismCore script -->
<script src = "../../../Core/live2dcubismcore.js"></script>
<!-- Build script -->
<script src = "./dist/bundle.js"></script>
</head>
<body>
<div class="MainContainer">
<div class="VideoSymbolContainer">
<canvas id="myCanvas"></canvas>
<div class="CommentArea">
<section class="CommentPostContainer">
<div class="CommentPostContainer-inner">
<div class="CommentPostContainer-commentInput">
<div class="RecaptchaRequirer-comment">
<div class="CommentInput">
<input class="CommentInput-textarea" id="CommentInputArea" placeholder="ã³ã¡ã³ã" maxlength="75" rows="1"></input>
</div>
</div>
<div class="RecaptchaRequirer-button">
<button type="button" class="CommentPostButton" id="comment-post-button">
ã³ã¡ã³ã
</button>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</body>
</html>
index.cssã¯æ¬¡ã®æ§ã«ä¿®æ£
html, body {
font-family: Avenir,Lato,-apple-system,BlinkMacSystemFont,Helvetica Neue,Hiragino Kaku Gothic ProN,Meiryo,ã¡ã€ãªãª,sans-serif;
margin: 0;
overflow: hidden;
background-color: #f4f4f4;
}
.MainContainer {
display: flex;
}
.VideoSymbolContainer {
display: flex;
flex-direction: column;
}
/* ãã£ã³ãã¹ã®å€§ãã */
#myCanvas {
display: block;
margin: 16px 16px 0px 16px;
}
/* ã³ã¡ã³ããæã€ãšãã */
.CommentArea {
margin: 0px 16px 16px 16px;
background-color: #fff;
}
.CommentPostContainer {
margin-right: 8px;
width: 100%;
display: block;
}
.CommentPostContainer-inner {
display: flex;
}
.CommentPostContainer-commentInput {
display: flex;
flex-direction: row;
background-color: #fff;
border-bottom: 2px solid #ddd;
border-top: 2px solid #ddd;
flex: 1 0 1px;
min-width: 1px;
vertical-align: middle;
}
.RecaptchaRequirer-comment {
margin: 8px 0px 8px 16px;
width: 100%;
}
.RecaptchaRequirer-button {
margin: 8px 16px 8px 0px;
}
.CommentInput {
font-size: 14px;
line-height: 20px;
padding: 4px 8px;
border-top: 2px solid #ddd;
border-bottom: 2px solid #ddd;
border-left: 2px solid #ddd;
border-radius: 4px 0px 0px 4px;
}
.CommentInput-textarea {
background-color: transparent;
border: none;
outline: none;
display: inline-block;
height: 100%;
line-height: inherit;
overflow: hidden;
padding: 0;
resize: none;
vertical-align: top;
white-space: pre;
width: 100%;
}
.CommentPostButton {
align-items: center;
background-color: #007cff;
color: #fff;
cursor: pointer;
display: flex;
font-size: 12px;
font-weight: 700;
height: 32px;
justify-content: center;
line-height: 1.5;
width: 92px;
border-top: 2px solid #ddd;
border-right: 2px solid #ddd;
border-bottom: 2px solid #ddd;
border-left: none;
border-radius: 0px 4px 4px 0px;
}
ãŸããã£ãŒã¡ããã衚瀺ãããŠããcanvasã¯TypeScriptåŽã§å®è£
ãããŠããã®ã§ãã¡ããä¿®æ£ããŸãã
live2d-for-web\Samples\TypeScript\Demo\src\lappdelegate.ts
/**
* APPã«å¿
èŠãªç©ãåæåããã
*/
public initialize(): boolean {
// ãã£ã³ãã¹ã®äœæ
// canvas = document.createElement('canvas');
canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
ãã
...
// ãã£ã³ãã¹ã DOM ã«è¿œå
// document.body.appendChild(canvas);createElementãããŠãããšãããgetElementByIdã§htmlã§äœãããŠããcanvasã®æ¹ãåç §ããããã«ããŸãã
ãŸãããã£ã³ãã¹ãDOMã«è¿œå ãããšãããããã®ã§ãããã³ã¡ã³ãã¢ãŠãããŠãããŸãã

ãŸã canvasã倧ããã§ããã
ãã¡ããã³ãã³åç»ã®åç»ãèŠãæã£ãœãããæãã«ä¿®æ£ããŸãã
live2d-for-web\Samples\TypeScript\Demo\src\lappdelegate.ts
ã«ããã private _resizeCanvas(): void ãæ¬¡ã®ããã«ä¿®æ£ããŸãã
export class LAppDelegate {
...
/**
* Resize the canvas to fill the screen.
*/
private _resizeCanvas(): void {
let {canvasWidth, canvasHeight} = resizeCanvas(window.innerWidth - 40, window.innerHeight - 80)
canvas.width = canvasWidth
canvas.height = canvasHeight
}
...
}
/** 16:9ã®ã¢ã¹ãã¯ãæ¯ã®Width, Heightãè¿ããŸã */
const resizeCanvas = (width: number, height: number) => {
const ratio = 16 / 9;
let canvasWidth = 0;
let canvasHeight = 0;
if (width / height > ratio) {
canvasWidth = height * ratio;
canvasHeight = height;
} else {
canvasWidth = width;
canvasHeight = width / ratio;
}
return {canvasWidth, canvasHeight}
}canvasã®å€§ããã16:9ã«ããããã«é¢æ°ã远å ããŠãŸãã

ãããšãããæãã«ããã£ãœããªããŸããã
次ã¯ãã£ãŒã¡ããããã£ãšãã£ã¡ã«å¯ã£ãŠããããŸãããã
live2d-for-web\Samples\TypeScript\Demo\src\lapplive2dmanager.ts ã®
public inUpdate(): void ã¡ãœãããä¿®æ£ããŸãã
export class LAppLive2DManager {
...
/**
* ç»é¢ãæŽæ°ãããšãã®åŠç
* ã¢ãã«ã®æŽæ°åŠçåã³æç»åŠçãè¡ã
*/
public onUpdate(): void {
const { width, height } = canvas;
const modelCount: number = this._models.getSize();
for (let i = 0; i < modelCount; ++i) {
const projection: CubismMatrix44 = new CubismMatrix44();
const model: LAppModel = this.getModel(i);
if (model.getModel()) {
if (model.getModel().getCanvasWidth() > 1.0 && width < height) {
// 暪ã«é·ãã¢ãã«ã瞊é·ãŠã£ã³ããŠã«è¡šç€ºããéã¢ãã«ã®æšªãµã€ãºã§scaleãç®åºãã
model.getModelMatrix().setWidth(2.0);
// projection.scale(1.0, width / height);
projection.scale(2.0, width / height * 2.0); //衚瀺ãµã€ãºãïŒåã«å€æŽ
projection.translate(0, -2.5);
} else {
// projection.scale(height / width, 1.0);
projection.scale(2.0, width / height * 2.0); //衚瀺ãµã€ãºãïŒåã«å€æŽ
projection.translate(0, -2.5); //é«ãããªãããããæãã«èª¿æŽ
}
// å¿
èŠãããã°ããã§ä¹ç®
if (this._viewMatrix != null) {
projection.multiplyByMatrix(this._viewMatrix);
}
}
model.update();
model.draw(projection); // åç
§æž¡ããªã®ã§projectionã¯å€è³ªããã
}
}
...
}
ãã®ãã£ãŒã¡ããã¢ãã«ã¯ããŸã ããããæºããã ãã§ãç®ãããå£ãã¯ã¯ããŠãããŸããã
ãªã®ã§å®è£
ããŸãããã
ç®ãããå®è£ ãã
ç®ããã¯çµæ§ç°¡åã§ãfee.model3.jsonãã¡ã€ã«ã®äžèº«ããã®ããã«ä¿®æ£ããŸãã
"Groups": [
{
"Target": "Parameter",
"Name": "EyeBlink",
"Ids": [
"ParamEyeLOpen",
"ParamEyeROpen"
]
}
]ããã远å ããã ãã§ç®ãããããŠãããŸãã坿ãã§ãã(*ÂŽâïœ)
å£ãã¯ãå®è£ ãã
次ã«å£ãã¯ã®å®è£
ãããŸãã
å£ãã¯ã¯Live2dã§ã¯ããªããã·ã³ã¯ããšåŒãã§ããã®ã§ãã¡ãã以åŸãªããã·ã³ã¯ãšåŒã³ãŸãã
ãã¡ãã®ãªããã·ã³ã¯ãå®è£ ããããã«ããŸã㯠fee.model3.jsonã«è¿œå å®è£ ããŸãã
"Groups": [
{
"Target": "Parameter",
"Name": "LipSync",
"Ids": [
"ParamMouthOpenY"
]
},
{
"Target": "Parameter",
"Name": "EyeBlink",
"Ids": [
"ParamEyeLOpen",
"ParamEyeROpen"
]
}
]次ã«é³å£°ã«åãããŠãªããã·ã³ã¯ããŠãããããã«ãtsãã¡ã€ã«ãä¿®æ£ããŸãã
live2d-for-web\Samples\TypeScript\Demo\src\lappmodel.ts ãä¿®æ£ããŸãã
/**
* ãŠãŒã¶ãŒãå®éã«äœ¿çšããã¢ãã«ã®å®è£
ã¯ã©ã¹<br>
* ã¢ãã«çæãæ©èœã³ã³ããŒãã³ãçæãæŽæ°åŠçãšã¬ã³ããªã³ã°ã®åŒã³åºããè¡ãã
*/
export class LAppModel extends CubismUserModel {
...
/**
* æŽæ°
*/
public update(): void {
...
// ãªããã·ã³ã¯ã®èšå®
if (this._lipsync) {
let value = 1.0; // ãªã¢ã«ã¿ã€ã ã§ãªããã·ã³ã¯ãè¡ãå Žåãã·ã¹ãã ããé³éãååŸããŠã0~1ã®ç¯å²ã§å€ãå
¥åããŸãã
this._wavFileHandler.update(deltaTimeSeconds);
value = this._wavFileHandler.getRms(); // ããã§ã©ããããå£ãéãããã®ãã©ã¡ãŒã¿ãååŸã§ãã
value = value * 4;
this._model.addParameterValueById(this._lipSyncIds.at(0), value, 0.8);
}
...
}
/**
* (èªå)ãªããã·ã³ã¯ã®é³ãæµããŸãã
* @param path Audip_path
*/
public async startVoice() {
document.body.addEventListener("click", () => {
const path = "test.wav"
const voice = new Audio(path);
voice.play();
this._wavFileHandler.start(path); // ãªããã·ã³ã¯ãå§ããããšãããã§æå®ã§ãã
})
}
...
/**
* ãã¯ã¹ãã£ãŠãããã«ãã¯ã¹ãã£ãããŒããã
*/
private setupTextures(): void {
...
this.startVoice()
}Demoé äžã«é©åœã« test.wavãã¡ã€ã«ã眮ããç»é¢ãã¯ãªãã¯ãããšãªããã·ã³ã¯ãããã確èªã§ããŸãã

ãªããã·ã³ã¯ãããæåã§ãã
å£ãããããããŠãå Žåãvalue = value * 4;ãã®å€ãããã¡ãã£ãšå€§ããããŠã¿ãŸãããã
ãããããšã倧ããå£ãéããŠããããšæããŸãã
å€éšã®ãã¡ã€ã«ã§ãªããã·ã³ã¯ããã
ããŒã«ã«ã«ããwavãã¡ã€ã«ã§ãªããã·ã³ã¯ãããããšãã§ããŸããã
次ã¯ãå€éšããããã£ãŠããé³å£°ã§ãªããã·ã³ã¯ãããŠãããŸãã
ããããå ã»ã©å®è£ ãã startVoiceã¡ãœãããæ¬¡ã®ããã«ä¿®æ£ããŸãã
/**
* (èªå)ãªããã·ã³ã¯ã®é³ãæµããŸãã
* @param path Audip_path
*/
public async startVoice() {
/** Ctrl+Enter ãããæãclickButtonããããŸã */
const inputArea = document.getElementById("CommentInputArea")
inputArea.addEventListener("keypress", (e) => {
if (e.ctrlKey === true && e.keyCode === 10) {
clickButton()
}
})
/** ButtonãæŒãããæãclickButtonããããŸã */
document.getElementById('comment-post-button').addEventListener('click', () => clickButton())
/** å
¥åãããããã¹ããããã£ãŒã¡ãããåã£ãŠãããŸãã */
const clickButton = () => {
const inputText = getInputText()
if (inputText === "" || !inputText) return
requestMessage(inputText)
.then((fileName) => {
console.log(fileName)
const path = `http://127.0.0.1:5000/audio?file_name=${fileName}`
const voice = new Audio(path);
console.log(voice)
voice.play();
this._wavFileHandler.start(path);
})
}
/** ããã¹ããéä¿¡ããŸã */
const getInputText = () => {
const comment = <HTMLInputElement>document.getElementById("CommentInputArea")
const inputText = comment.value
if (inputText === "" || !inputText) return
comment.value = ""
return inputText
}
// ã¡ãã»ãŒãžãéã£ãŠäœæãããwavãã¡ã€ã«ã®ãã¡ã€ã«åãååŸããŸã
const requestMessage = async (message) => {
const url = "http://127.0.0.1:5000/getAudio";
const jsonData = {
"text": message,
};
const result = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(jsonData),
})
const json = await result.json();
return json["fileName"]
}
}ãã£ãŠããããšã¯ããã¿ã³ãæŒããããšãã«å ¥åãããŠããããã¹ããéä¿¡ããwavãã¡ã€ã«ãååŸãããããåçããŠããŸãã
ãããŸã§å®è£ ããããã³ã¡ã³ããã¿ã³ãæŒããšãã£ãŒã¡ãããåã£ãŠããããšæããŸãã
â»å¿ ãpythonãµãŒããŒãèµ·åãããç¶æ ã§è¡ã£ãŠãã ããã

ç¡äºã«å£ãã¯ããŠãããŸãã
ãã®å£ãã¯ã¯ãã¡ãã®èšäºã倧å€åèã«ãªããŸããã
ãã³ãã³åç»ã£ãœãã³ã¡ã³ããæµã
次ã«ããã£ãããããŸã§ãã£ãããã³ãã³åç»ã£ãœãã³ã¡ã³ããæµãããã§ãããã
ã£ãŠããšã§ã³ã¡ã³ããæµããããã«ããŸãã
ãã®èŸºã¯ãã¡ãã®èšäºããŸããŸãåèã«ããŸããã
gsapã§ãã³ãã³ã£ãœãã³ã¡ã³ããæµããŠãããŸãã
ãŸãhtmlã«æ¬¡ã®scriptã远å ããŸãã
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.5.1/gsap.min.js"></script>
</head>å ã»ã©ä¿®æ£ãã startVoiceã«æ¬¡ã®ã³ãŒãã远å ããŸãã
import gsap from 'gsap';
/**
* (èªå)ãªããã·ã³ã¯ã®é³ãæµããŸãã
* @param path Audip_path
*/
public async startVoice() {
/** Ctrl+Enter ãããæãclickButtonããããŸã */
const inputArea = document.getElementById("CommentInputArea")
inputArea.addEventListener("keypress", (e) => {
if (e.ctrlKey === true && e.keyCode === 10) {
clickButton()
}
})
/** ButtonãæŒãããæãclickButtonããããŸã */
document.getElementById('comment-post-button').addEventListener('click', () => clickButton())
/** å
¥åãããããã¹ããããã£ãŒã¡ãããåã£ãŠãããŸãã */
const clickButton = () => {
const inputText = getInputText()
if (inputText === "" || !inputText) return
createText(inputText) // ðããã远å ããŸãã
requestMessage(inputText)
.then((fileName) => {
console.log(fileName)
const path = `http://127.0.0.1:5000/audio?file_name=${fileName}`
const voice = new Audio(path);
voice.play();
this._wavFileHandler.start(path);
})
}
/** ããã¹ããéä¿¡ããŸã */
const getInputText = () => {
const comment = <HTMLInputElement>document.getElementById("CommentInputArea")
const inputText = comment.value
if (inputText === "" || !inputText) return
comment.value = ""
return inputText
}
// ã¡ãã»ãŒãžãéã£ãŠäœæãããwavãã¡ã€ã«ã®ãã¡ã€ã«åãååŸããŸã
const requestMessage = async (message) => {
const url = "http://127.0.0.1:5000/getAudio";
const jsonData = {
"text": message,
};
const result = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(jsonData),
})
const json = await result.json();
return json["fileName"]
}
// ðããã远å ããŸããã
// ã³ã¡ã³ããæµããŸãã
let count = 0;
const createText = async (text) => {
let div_text = document.createElement('div');
div_text.id="text"+count; //ã¢ãã¡ãŒã·ã§ã³åŠçã§å¯Ÿè±¡ã®æå®ã«å¿
èŠãªidãèšå®
count++;
div_text.style.position = 'fixed'; //ããã¹ãã®ã¯äœçœ®ã絶察äœçœ®ã«ããããã®èšå®
div_text.style.whiteSpace = 'nowrap' //ç»é¢å³ç«¯ã§ã®æãè¿ãããªããç»é¢å€ãžã¯ã¿åºãããã«ãã
div_text.style.left = (document.documentElement.clientWidth) + 'px'; //åæç¶æ
ã®æšªæ¹åã®äœçœ®ã¯ç»é¢ã®å³ç«¯ã«èšå®
div_text.style.fontFamily = "ãã©ã®ãè§ãŽ"
div_text.style.fontSize = "24px"
div_text.style.color = "#fff"
div_text.style.fontWeight = "bold"
var random = Math.round( Math.random()* (document.documentElement.clientHeight / 2) );
div_text.style.top = random + 'px'; //åæç¶æ
ã®çžŠæ¹åã®äœçœ®ã¯ç»é¢ã®äžç«¯ããäžç«¯ã®éã«èšå®ïŒã©ã³ãã ãªé
眮ã«ïŒ
div_text.appendChild(document.createTextNode(text)); //ç»é¢äžã«è¡šç€ºãããããã¹ããèšå®
document.body.appendChild(div_text); //bodyçŽäžãžæ¿å
¥
//ã©ã€ãã©ãªãçšããããã¹ãç§»åã®ã¢ãã¡ãŒã·ã§ã³ïŒ durationã¯ã¢ãã¡ãŒã·ã§ã³ã®æéã
// 暪æ¹åã®ç§»åè·é¢ã¯ãç»é¢ã®æšªå¹
ïŒç»é¢ãæµããããã¹ãã®èŠçŽ ã®æšªå¹
ããç§»åäžã«æ¬¡ã®åé€åŠçããããªãããawait
await gsap.to("#"+div_text.id, {duration: 7, x: -1*(document.documentElement.clientWidth+div_text.clientWidth), ease: Power0.easeNone});
div_text.parentNode.removeChild(div_text); //ç»é¢äžã®ç§»åçµäºåŸã«åé€
}
}ãŸããgsapã©ã€ãã©ãªã远å ããå¿ èŠãããããã
$ yarn add gsapãè¡ããŸãã
ããã§ãã³ãã³åç»ã£ãœãã³ã¡ã³ããæµããŸãã

ãã¹ã¯ãããã¢ããªåããã
ãããŸã§æ¥ããããã©ã¹ãã§ããæåŸã«ãã®webã¢ããªã±ãŒã·ã§ã³ããã¹ã¯ãããã¢ããªåãããŸãããã
䜿ãã®ã¯Electronãšãããã¬ã€ã ã¯ãŒã¯ã§ãã
 ãã¡ãããwebã§äœ¿ãããhtml, cssã§ãã¹ã¯ãããã¢ããªãäœæã§ãããã¬ã€ã ã¯ãŒã¯ã«ãªããŸãã
ã£ãŠããšã§ãã¡ããå°å ¥ããŸãã
$ yarn add electron
$ yarn electron ãããããšãã®ãããªç»é¢ãäœæãããŸãã

ãã¡ãããã£ãŒã¡ããã®ç»é¢ã«ããŠãããŸãã
ãŸããpackage.jsonã«æ¬¡ã®æ§ã«ä¿®æ£ããŸãã
{
"main": "main.js",
}ãããŠãmain.jsãDemoé
äžã«äœæããŸãã
äžèº«ã¯ãããªæãã§ãã
// ã¢ããªã±ãŒã·ã§ã³äœæçšã®ã¢ãžã¥ãŒã«ãèªã¿èŸŒã¿
const { app, BrowserWindow, ipcMain, nativeTheme } = require("electron");
const path = require("path");
console.log("Let's play electron")
// ã¡ã€ã³ãŠã£ã³ããŠ
let mainWindow;
const createWindow = () => {
// ã¡ã€ã³ãŠã£ã³ããŠãäœæããŸã
mainWindow = new BrowserWindow({
width: 700,
height: 500,
webPreferences: {
// ããªããŒãã¹ã¯ãªããã¯ãã¬ã³ãã©ãŒããã»ã¹ãèªã¿èŸŒãŸããåã«å®è¡ããã
// ã¬ã³ãã©ãŒã®ã°ããŒãã«ïŒwindow ã document ãªã©ïŒãš Node.js ç°å¢ã®äž¡æ¹ã«ã¢ã¯ã»ã¹ã§ããŸãã
preload: path.join(__dirname, "preload.js"),
},
});
// ã¡ã€ã³ãŠã£ã³ããŠã«è¡šç€ºããURLãæå®ããŸã
// ïŒä»åã¯main.jsãšåããã£ã¬ã¯ããªã®index.htmlïŒ
mainWindow.loadFile("index.html");
nativeTheme.themeSource = 'dark'
// ã¡ã€ã³ãŠã£ã³ããŠãéãããããšãã®åŠç
mainWindow.on("closed", () => {
mainWindow = null;
});
};
// åæåãå®äºããæã®åŠç
app.whenReady().then(() => {
createWindow();
// ã¢ããªã±ãŒã·ã§ã³ãã¢ã¯ãã£ãã«ãªã£ãæã®åŠç(Macã ãšãDockãã¯ãªãã¯ãããæïŒ
app.on("activate", () => {
// ã¡ã€ã³ãŠã£ã³ããŠãæ¶ããŠããå Žåã¯å床ã¡ã€ã³ãŠã£ã³ããŠãäœæãã
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
// å
šãŠã®ãŠã£ã³ããŠãéãããšãã®åŠç
app.on("window-all-closed", () => {
// macOSã®ãšã以å€ã¯ã¢ããªã±ãŒã·ã§ã³ãçµäºãããŸã
if (process.platform !== "darwin") {
app.quit();
}
});ãã®ç¶æ ã§ã次ã®ã³ãã³ããå®è¡ããŸãã
$ yarn electron main
ããã§ãã¹ã¯ãããã¢ããªåã®å®æã§ãã
ãŸãšã
以äžããç§ããã£ãŒã¡ããã®ãã¹ã¯ãããã¢ããªåã®å
šå®¹ã§ãã
ãããã ã£ãã§ããããïŒ
åºæ¥ãã ãåãããšããããããšæã£ã人åãã«èª¬æããŠã¿ãŸããã
èªåã®èª¬æäžè¶³ãã誀åè±åãã³ãŒãã®ããã³ãçã
ãããšæããŸãã
ããã¯ä»åŸã®å®è£
ã§çŽããŠããããã®ãšãäœãããã°æ°è»œã«Twitterã®DMãšãã§è³ªåããŠãã ãããšå¬ããã§ã(*ÂŽâïœ)
Twitter ->Â https://twitter.com/tktksnsn07
倧æ ã¯åºæ¥ãã®ã§ãä»åŸã¯ããã¹ãã«ããææ
åæã®å®è£
(äžåºŠå®è£
ãããã©ç²ŸåºŠãããŸãã¡ã ã£ãâŠ)
ãã£ãŒã¡ããã®è²ããªè¡šæ
ã®è¿œå
ãã£ãšããUI
ãªã©ãªã©ãç®æããŠéçºãé²ããŠããããšæããŸãïŒ
ãã®èšäºãæšããã£ã©ãäœãã®ã«åèã«ãªãã°å¹žãã§ãã
ã§ã¯ãŸãã©ããã§äŒããããšã楜ãã¿ã«ããŠããŸãããã·