HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2025
0224
youdai自作システム上で動作するVtuberのモデルの口パクを「音声とシンクロする形」で制御したいです7未解決


youdai

リンク

2025/2/24(Mon) 14:04:15|NO.103115

自作システム上で動作するVtuberのモデルの口パクを「音声とシンクロする形」で制御したいと思いました。
要はそれは、wavプレイヤーのボリュームメーターと仕組みは同じなのではないかと思いました。
それで以下のような機能を実装したいと思ったのですが、以下の2点をどう実装したらいいのかよく分かりませんでした。

1.録音済みの音声のwavファイルの「音の大きさ」を、音声の再生と同期した形で取得したい
2.マイクから入力される音声の「音の大きさ」を、リアルタイムで取得したい

2の部分はWindows APIやCOM等で用意されていそうな気もするのですが、専門外過ぎてよく分かりませんでした。
動作させる対象のPC環境はWindows 10で、オーディオ環境はごく普通のWDMです。DirectXのDirectSound系でも構いません。

ご存じの方がいましたら、アドバイスお願い致します。(アドバイスは1番でも2番でも両方でも、分かる部分だけで構いません)



この記事に返信する


youdai

リンク

2025/2/24(Mon) 17:13:17|NO.103116

対象のPC環境をもう少し詳しく調べてみました。

動作可のもの
・WASAPI
・DirectX(DirectSound)
・WDM

この内、WDMが古い規格で、WASAPIが新しい規格のようです。
Windows 10で動作させるので、WASAPIの方がいいのかもしれません。

動作不可のもの
・ASIO



GENKI

リンク

2025/2/24(Mon) 22:09:24|NO.103118

VTuberなどに使われる主要なリップシンク方式の比較メモ
https://qiita.com/nkjzm/items/5fb4f4dbcdaa9bc7cc34

こちらのサイトによると音圧の大小による口パクでも十分らしくみえるようですね。
マイクからの音の大きさをリアルタイムで取得する方法については、こちらも参照してみてください。

https://hsp.tv/play/pforum.php?mode=pastwch&num=84094
https://mclab.uunyan.com/lab/hspneta/neta006.htm



youdai

リンク

2025/2/25(Tue) 18:09:28|NO.103123

>GENKI さん

アドバイスありがとうございます!
GENKIさんの「音声の入力」のページは質問文を書き込んだ後に偶然発見して、大変参考にさせて貰ってます。現在研究中で、おそらくこれでリアルタイム口パクについては実装できそうです。

mod_riffについては、RIFFというものそのものを知らなかったので、こういう使い方をすればいいモジュールというのを初めて知りました。
これからmod_riffについても研究させて貰おうと思います。

Vtuberの口パクについてですが、自分もですが他のVtuberの子たちもなかなか思った通りの口パクにならず苦労しているようです。



usagi

リンク

2025/2/27(Thu) 00:53:34|NO.103136

こんにちわ
面白い事おこなってますね〜。
マイクなら標準搭載されてるwinmm使うのが簡単かと思います。
宜しかったら参考にどうぞ。

// 簡易マイク入力モジュール // ---------------------------------------------- #module _WaveIn_ #uselib "WINMM.DLL" #func waveInOpen "waveInOpen" var,int,var,int,int,int #func waveInClose "waveInClose" int #func waveInStart "waveInStart" int #func waveInReset "waveInReset" int #func waveInPrepareHeader "waveInPrepareHeader" int,var,int #func waveInUnprepareHeader "waveInUnprepareHeader" int,var,int #func waveInAddBuffer "waveInAddBuffer" int,var,int #deffunc mic_open array _buff, int _rate dup m_buff, _buff // 入力のオープン(既定のマイクとフォーマットを設定)PCM: ? KHz 16bit mono hwi = 0 : wfx = 1 | (1 << 16), _rate, _rate * 2, 2 | (16 << 16), 0 waveInOpen hwi, /*WAVE_MAPPER*/ 0xFFFFFFFF, wfx, hwnd, 0, /*CALLBACK_WINDOW*/ 0x10000 // マイク入力用バッファ作成(ダブル) bufferLength = length(_buff) * 2 sdim buff1, bufferLength sdim buff2, bufferLength wh1 = varptr(buff1), varsize(buff1), 0,0,0,0,0,0 wh2 = varptr(buff2), varsize(buff2), 0,0,0,0,0,0 waveInPrepareHeader hwi, wh1, 32 : waveInAddBuffer hwi, wh1, 32 waveInPrepareHeader hwi, wh2, 32 : waveInAddBuffer hwi, wh2, 32 // 入力スタート waveInStart hwi : if stat { dialog "ERROR: waveInStart("+stat+")" : end } oncmd gosub *On_MM_WIM_DATA, /*MM_WIM_DATA*/0x03C0 return *On_MM_WIM_DATA dupptr wh, lparam, 32 : dupptr buff, wh.0, wh.1, 2 foreach m_buff : m_buff.cnt = double(wpeek(buff, cnt * 2) << 16 >> 16) / 32768.0 : loop peak = 0.0 : foreach m_buff : peak = limitf(peak, absf(m_buff.cnt)) : loop rms = 0.0 : foreach m_buff : rms += m_buff.cnt * m_buff.cnt : loop : rms = sqrt(rms / length(m_buff)) waveInAddBuffer wparam, wh, 32 return #defcfunc mic_peak return peak #defcfunc mic_rms return rms #deffunc local exit onexit waveInReset hwi : waveInUnprepareHeader hwi, wh, 32 : waveInClose hwi : return #global // サンプル // ---------------------------------------------- #use a2d // 画面とバッファ screen 0, 320,512 : title "マイクで簡易口パク" alCreateImageByFile 1, dir_tv+"up_tar.png" : alCreateImage 0, 512, 512 : alFont // 設定値 gain = 10.0 // マイクの入力感度 dumping = 0.3 // 口の滑らかさ vN = 52, 53, 70, 45, 52, 53, 30, 45 // 口(閉) 頂点データ 100*100 vO = 50, 30, 62, 45, 50, 58, 38, 45 // 口(お) vE = 50, 35, 80, 43, 50, 55, 20, 43 // 口(え) vA = 50, 30, 75, 47, 50, 65, 25, 47 // 口(あ) // マイクオープン ddim buff, 256 : mic_open buff, 8000 // ★追加した命令 *MAIN // アップデート // ---------------------------------------------- // 感度調整 stick pad : gain = limitf(gain + 0.5*(pad>>2&1)-0.5*(pad&1), 0, 100) // まばたき更新 blink = limit(blink-1, 0, 2) : if rnd(120) = 0 { blink = 2 } // 口の頂点ブレンド更新 blend = limitf(blend + (mic_rms() * gain - blend) * dumping, 0.0, 1.0) // ★入力の二乗平均を使う repeat 1 if blend <= 0.25 { foreach vN : vC.cnt = vN.cnt : loop : break } if blend <= 0.50 { foreach vN : vC.cnt = int((double(vO.cnt)-double(vN.cnt))*(blend-0.25)*4. + double(vN.cnt)) : loop : break } if blend <= 0.75 { foreach vN : vC.cnt = int((double(vE.cnt)-double(vO.cnt))*(blend-0.50)*4. + double(vO.cnt)) : loop : break } if blend <= 1.00 { foreach vN : vC.cnt = int((double(vA.cnt)-double(vE.cnt))*(blend-0.75)*4. + double(vE.cnt)) : loop : break } loop // ドロー // ---------------------------------------------- alErase : alResetTransMode // キャラ描画 alTransModeOffsetRotateZoom 0,0, 0, 0.5,0.5 // 頭体 alCopyImageToImage 1,0, 0,0, 640, 1024 alTransModeOffsetRotateZoom 64,165, 0, 0.5,0.5 // 眉 alCopyImageToImage 1,0, 0,0, 385, 128, 640, 0 alTransModeOffsetRotateZoom 65,217, 0, 0.5,0.5 // 目 alCopyImageToImage 1,0, 0,0, 385, 128, 640, 512+blink*128 alTransModeOffsetRotateZoom 123,271, 0, 0.7, 0.7 // 口描画 alColor 212,140,143 : alFillClosedCurve vC, 4, 0.8 // 口の塗 alColor : alDrawClosedCurve vC, 4, 0.8 // 口の線 // 入力波形描画 alTransModeOffsetRotateZoom ,32,,1.25, 0.3 : alColor 84,84,255 x2 = 0 : y2 = 100 : repeat length(buff) : x1 = x2 : y1 = y2 : x2 = cnt : y2 = -buff.cnt*100.0+100 : alDrawLine x1, y1, x2, y2 : loop // 情報描画 alResetTransMode alColor 255,122,0,64 : alFillRect 0,0, 320,16 alColor 255,122,0 : repeat 3,1 : alDrawLine 80*cnt, 0, 80*cnt, 16 : loop txt = "閉じ", "お","え","あ" : repeat 4 : alDrawText txt.cnt, 80*cnt, 0, 80,16,1 :loop alDrawText strf("口パク感度(← →): %3.1f", gain), 0,24 alColor 255,122,0,128 : alFillRect 0,4, blend*320,8 // 画面更新と待ち alCopyImageToScreen 0, 0 : redraw : await 16 : redraw 0 : rgbcolor $FFFFFF : boxf goto *MAIN

★余談★
音量は信号のピークを使うよりRMS使った方が好みです。
また、音量だと口が開きっぱなしになってしまいますので、
ある程度"音素"は判定した方が良いですね。



GENKI

リンク

2025/2/27(Thu) 22:16:40|NO.103141

くちが開きっぱなしになる問題の対策の一つとして、これも使えるかもしれません。

デジタルフィルタ(mod_BQFモジュール)
https://mclab.uunyan.com/dl/dl42.htm



youdai

リンク

2025/2/28(Fri) 18:15:36|NO.103155

> usagi さん

usagiさん、アドバイスありがとうございます!

これは凄い! これは凄いですね!
これは画像をオリジナルのものに差し替えれば、そのままVtuberとしてデビューできてしまうほどだと思います。

凄く重要な技術だと思うので、このスクリプトに関する技術動画を撮ったのですが、これを動画配信で流していいでしょうか?

⚫研究中に気づいたこと「口の開き方によってキャラクターを差別化できる」

usagiさんとGENKIさんのスクリプトを研究していて気づいたのですが、usagiさんの以下の部分は特に重要だと思いました。


// 口の頂点ブレンド更新 blend = limitf(blend + (mic_rms() * gain - blend) * dumping, 0.0, 1.0) // ★入力の二乗平均を使う

例えば上記の部分を以下のように、


// 口の頂点ブレンド更新 blend = limitf(blend + (mic_peak() * gain - blend) * dumping, 0.0, 1.0) // ★入力のピークを使う

変更した場合、当然、口が開きっぱなしになるわけですが、キャラクターの表現として「アホの娘っぽさ」が生まれ、
この部分の「口の開き方補正によって、キャラクター性に変化を持たせることができる」ことが分かりました。

つまり、mic_rms()とmic_peak()の線形補完によって「キャラクター性」の具合がパラメータとして調整できるわけです。
これはかなり大きな発見でした。

この部分の更なる調整によって「口の開き方によるキャクラター性の差別化」が可能となるかもしれません。

そこでGENKIさんのスクリプトのフィルタリング部分も組み合わせれば、かなり自由な調整ができそうです。
お二人のアドバイスは異なるアプローチ(のように見える)でのアドバイスのはずなのに、こういう形で繋がるのはかなり不思議な感じです。

今回の研究は思った以上に大きな発見が多いです。



usagi

リンク

2025/3/3(Mon) 08:16:30|NO.103192

返信遅れてすみません。私が掲示板に書き込んだものはご自由にお使い下さいませ。
※参考がある場合は必ずコメント記載します。
blendの部分は滑らかにするために、線形補完にバネ組み合わせたイメージです。
何か参考になれば幸いです。



記事削除

記事NO.パスワード
(質問が解決したスレッドは他の利用者に活用してもらうため、削除しないようお願いします)

NO.103115への返信

マスコット

好きなマスコットを選んでください。

名前

e-mail
HOME
  1. 初めて利用する方は、HSP3掲示板の使い方をお読みください。
  2. 不要部分の多い長いスクリプトの投稿は ご遠慮ください。
  3. 書き込みは自動改行されません。適度に改行を入れてください。
  4. スクリプトは小文字の<pre>〜</pre>で囲むと見やすく表示できます。

削除用パスワード

解決したら質問者本人がここをチェックしてください。

エラー発生時、再送信すると二重送信になることがあります。
回答が得られたら、お礼書き込み時に[解決]チェックしてください。
SPAM防止のためURLから始まる文章は投稿できません。
SPAM防止のため英文字のみの本文を投稿することはできません。

ONION software Copyright 1997-2023(c) All rights reserved.