[Beta version] Update to Cubism 4 SDK for Web R3 Beta1

translate
ito 2021-05-13 12:08:19 +09:00
parent 4902c91fda
commit dc3dcbc2ca
12 changed files with 451 additions and 13 deletions

View File

@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [4-r.3-beta.1] - 2021-05-13
### Added
* Add the sample to manipulate the lip-sync from a waveform on the audio file(.wav).
* Add sample voices to `Haru`.
## [4-r.2] - 2021-03-09
### Added
@ -73,7 +81,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Fix issue with reloading model images in WebKit.
[4-r.3-beta.1]: https://github.com/Live2D/CubismWebSamples/compare/4-r.2...4-r.3-beta.1
[4-r.2]: https://github.com/Live2D/CubismWebSamples/compare/4-r.1...4-r.2
[4-r.1]: https://github.com/Live2D/CubismWebSamples/compare/4-beta.2...4-r.1
[4-beta.2]: https://github.com/Live2D/CubismWebSamples/compare/4-beta.1...4-beta.2

View File

@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2021-03-09
### Added
* Add funtcions for Viewer.
* `csmGetParameterKeyCounts`
* `csmGetParameterKeyValues`
### Changed
* Update Core version to `04.01.0000`.
## 2020-01-30
### Added

@ -1 +1 @@
Subproject commit 4b2130bca984e1a5c3714fbe1fae201bc4cff452
Subproject commit 8ac72d40bc0a22eb06742c7062614457589bc822

View File

@ -6,6 +6,7 @@ Cubism Web Samples is included in Live2D Cubism Components.
Cubism Web Samples は Live2D Cubism Components に含まれます。
Cubism Web Samples 包括在 Live2D Cubism Components 中。
## Cubism SDK Release License
@ -17,6 +18,9 @@ Cubism Web Samples は Live2D Cubism Components に含まれます。
* [Cubism SDK リリースライセンス](https://www.live2d.com/ja/download/cubism-sdk/release-license/)
如果您的企业在最近一个会计年度的销售额达到或超过1000万日元您必须得到Cubism SDK的出版授权许可出版许可协议
* [Cubism SDK发行许可证](https://www.live2d.com/zh-CHS/download/cubism-sdk/release-license/)
## Live2D Open Software License
@ -24,6 +28,7 @@ Live2D Cubism Components is available under Live2D Open Software License.
* [Live2D Open Software License](https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html)
* [Live2D Open Software 使用許諾契約書](https://www.live2d.com/eula/live2d-open-software-license-agreement_jp.html)
* [Live2D Open Software 使用授权协议](https://www.live2d.com/eula/live2d-open-software-license-agreement_cn.html)
## Live2D Proprietary Software License
@ -32,6 +37,7 @@ Live2D Cubism Core is available under Live2D Proprietary Software License.
* [Live2D Proprietary Software License Agreement](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html)
* [Live2D Proprietary Software 使用許諾契約書](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_jp.html)
* [Live2D Proprietary Software 使用授权协议](https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_cn.html)
## Free Material License
@ -40,6 +46,7 @@ Live2D models listed below are available under Free Material License.
* [Free Material License Agreement](https://www.live2d.com/eula/live2d-free-material-license-agreement_en.html)
* [無償提供マテリアルの使用許諾契約書](https://www.live2d.com/eula/live2d-free-material-license-agreement_jp.html)
* [无偿提供素材使用授权协议](https://www.live2d.com/eula/live2d-free-material-license-agreement_cn.html)
```
Samples/Resources/Haru

View File

@ -1,9 +1,13 @@
# Cubism Web Samples
# \[Beta Version\] Cubism Web Samples
Live2D Cubism 4 Editor で出力したモデルを表示するアプリケーションのサンプル実装です。
Cubism Web Framework および Live2D Cubism Core と組み合わせて使用します。
**本 SDK は、 Beta バージョンとなります。先行して新機能を取り込んでいるため、不安定な挙動を示す場合がございます。安定した製品をお求めの方は、公式サイトから配布されている正式版のパッケージ又は `develop` `master` ブランチをご利用ください。**
**[Beta Version] の SDK の不具合、各種ご意見等に関しましては、 Live2D コミュニティ にてご連絡ください。直接のコードに対する指摘、修正等は、直接 Pull requests としてご投稿ください。**
## ライセンス
@ -80,10 +84,10 @@ NOTE: デバック用の設定は、`.vscode/launch.json` に記述していま
### Node.js
* 15.11.0
* 14.16.0
* 12.21.0
* 10.24.0
* 15.14.0
* 14.16.1
* 12.22.1
* 10.24.1
## 動作確認環境

View File

@ -23,10 +23,14 @@
{"File":"motions/haru_g_m15.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5}
],
"TapBody": [
{"File":"motions/haru_g_m06.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5},
{"File":"motions/haru_g_m09.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5},
{"File":"motions/haru_g_m20.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5},
{"File":"motions/haru_g_m26.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5}
{"File":"motions/haru_g_m06.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5
,"Sound": "sounds/haru_normal_01.wav"},
{"File":"motions/haru_g_m09.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5
,"Sound": "sounds/haru_normal_02.wav"},
{"File":"motions/haru_g_m20.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5
,"Sound": "sounds/haru_normal_03.wav"},
{"File":"motions/haru_g_m26.motion3.json" ,"FadeInTime":0.5, "FadeOutTime":0.5
,"Sound": "sounds/haru_normal_04.wav"}
]
},
"UserData": "Haru.userdata3.json"
@ -52,4 +56,4 @@
{"Name":"Head", "Id":"HitArea"},
{"Name":"Body", "Id":"HitArea2"}
]
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -38,6 +38,7 @@ import * as LAppDefine from './lappdefine';
import { canvas, frameBuffer, gl, LAppDelegate } from './lappdelegate';
import { LAppPal } from './lapppal';
import { TextureInfo } from './lapptexturemanager';
import { LAppWavFileHandler } from './lappwavfilehandler';
enum LoadStep {
LoadAssets,
@ -507,7 +508,10 @@ export class LAppModel extends CubismUserModel {
// リップシンクの設定
if (this._lipsync) {
const value = 0; // リアルタイムでリップシンクを行う場合、システムから音量を取得して、0~1の範囲で値を入力します。
let value = 0.0; // リアルタイムでリップシンクを行う場合、システムから音量を取得して、0~1の範囲で値を入力します。
this._wavFileHandler.update(deltaTimeSeconds);
value = this._wavFileHandler.getRms();
for (let i = 0; i < this._lipSyncIds.getSize(); ++i) {
this._model.addParameterValueById(this._lipSyncIds.at(i), value, 0.8);
@ -583,6 +587,14 @@ export class LAppModel extends CubismUserModel {
motion.setFinishedMotionHandler(onFinishedMotionHandler);
}
//voice
const voice = this._modelSetting.getMotionSoundFileName(group, no);
if (voice.localeCompare('') != 0) {
let path = voice;
path = this._modelHomeDir + path;
this._wavFileHandler.start(path);
}
if (this._debugMode) {
LAppPal.printMessage(`[APP]start motion: [${group}_${no}`);
}
@ -843,6 +855,7 @@ export class LAppModel extends CubismUserModel {
this._textureCount = 0;
this._motionCount = 0;
this._allMotionCount = 0;
this._wavFileHandler = new LAppWavFileHandler();
}
_modelSetting: ICubismModelSetting; // モデルセッティング情報
@ -870,4 +883,5 @@ export class LAppModel extends CubismUserModel {
_textureCount: number; // テクスチャカウント
_motionCount: number; // モーションデータカウント
_allMotionCount: number; // モーション総数
_wavFileHandler: LAppWavFileHandler; //wavファイルハンドラ
}

View File

@ -0,0 +1,388 @@
/**
* Copyright(c) Live2D Inc. All rights reserved.
*
* Use of this source code is governed by the Live2D Open Software license
* that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
*/
import { LAppPal } from './lapppal';
export let s_instance: LAppWavFileHandler = null;
export class LAppWavFileHandler {
/**
*
*
*
* @return
*/
public static getInstance(): LAppWavFileHandler {
if (s_instance == null) {
s_instance = new LAppWavFileHandler();
}
return s_instance;
}
/**
*
*/
public static releaseInstance(): void {
if (s_instance != null) {
s_instance = void 0;
}
s_instance = null;
}
public update(deltaTimeSeconds: number) {
let goalOffset: number;
let rms: number;
// データロード前/ファイル末尾に達した場合は更新しない
if (
this._pcmData == null ||
this._sampleOffset >= this._wavFileInfo._samplesPerChannel
) {
this._lastRms = 0.0;
return false;
}
// 経過時間後の状態を保持
this._userTimeSeconds += deltaTimeSeconds;
goalOffset = Math.floor(
this._userTimeSeconds * this._wavFileInfo._samplingRate
);
if (goalOffset > this._wavFileInfo._samplesPerChannel) {
goalOffset = this._wavFileInfo._samplesPerChannel;
}
// RMS計測
rms = 0.0;
for (
let channelCount = 0;
channelCount < this._wavFileInfo._numberOfChannels;
channelCount++
) {
for (
let sampleCount = this._sampleOffset;
sampleCount < goalOffset;
sampleCount++
) {
const pcm = this._pcmData[channelCount][sampleCount];
rms += pcm * pcm;
}
}
rms = Math.sqrt(
rms /
(this._wavFileInfo._numberOfChannels *
(goalOffset - this._sampleOffset))
);
this._lastRms = rms;
this._sampleOffset = goalOffset;
return true;
}
public start(filePath: string): void {
// サンプル位参照位置を初期化
this._sampleOffset = 0;
this._userTimeSeconds = 0.0;
// RMS値をリセット
this._lastRms = 0.0;
if (!this.loadWavFile(filePath)) {
return;
}
}
public getRms(): number {
return this._lastRms;
}
public loadWavFile(filePath: string): boolean {
let ret = false;
if (this._pcmData != null) {
this.releasePcmData();
}
// ファイルロード
const asyncFileLoad = async () => {
return fetch(filePath).then(responce => {
return responce.arrayBuffer();
});
};
const asyncWavFileManager = (async () => {
this._byteReader._fileByte = await asyncFileLoad();
this._byteReader._fileDataView = new DataView(this._byteReader._fileByte);
this._byteReader._fileSize = this._byteReader._fileByte.byteLength;
this._byteReader._readOffset = 0;
// ファイルロードに失敗しているか、先頭のシグネチャ"RIFF"を入れるサイズもない場合は失敗
if (
this._byteReader._fileByte == null ||
this._byteReader._fileSize < 4
) {
return false;
}
// ファイル名
this._wavFileInfo._fileName = filePath;
try {
// シグネチャ "RIFF"
if (!this._byteReader.getCheckSignature('RIFF')) {
ret = false;
throw new Error('Cannot find Signeture "RIFF".');
}
// ファイルサイズ-8読み飛ばし
this._byteReader.get32LittleEndian();
// シグネチャ "WAVE"
if (!this._byteReader.getCheckSignature('WAVE')) {
ret = false;
throw new Error('Cannot find Signeture "WAVE".');
}
// シグネチャ "fmt "
if (!this._byteReader.getCheckSignature('fmt ')) {
ret = false;
throw new Error('Cannot find Signeture "fmt".');
}
// fmtチャンクサイズ
const fmtChunkSize = this._byteReader.get32LittleEndian();
// フォーマットIDは1リニアPCM以外受け付けない
if (this._byteReader.get16LittleEndian() != 1) {
ret = false;
throw new Error('File is not linear PCM.');
}
// チャンネル数
this._wavFileInfo._numberOfChannels = this._byteReader.get16LittleEndian();
// サンプリングレート
this._wavFileInfo._samplingRate = this._byteReader.get32LittleEndian();
// データ速度[byte/sec](読み飛ばし)
this._byteReader.get32LittleEndian();
// ブロックサイズ(読み飛ばし)
this._byteReader.get16LittleEndian();
// 量子化ビット数
this._wavFileInfo._bitsPerSample = this._byteReader.get16LittleEndian();
// fmtチャンクの拡張部分の読み飛ばし
if (fmtChunkSize > 16) {
this._byteReader._readOffset += fmtChunkSize - 16;
}
// "data"チャンクが出現するまで読み飛ばし
while (
!this._byteReader.getCheckSignature('data') &&
this._byteReader._readOffset < this._byteReader._fileSize
) {
this._byteReader._readOffset +=
this._byteReader.get32LittleEndian() + 4;
}
// ファイル内に"data"チャンクが出現しなかった
if (this._byteReader._readOffset >= this._byteReader._fileSize) {
ret = false;
throw new Error('Cannot find "data" Chunk.');
}
// サンプル数
{
const dataChunkSize = this._byteReader.get32LittleEndian();
this._wavFileInfo._samplesPerChannel =
(dataChunkSize * 8) /
(this._wavFileInfo._bitsPerSample *
this._wavFileInfo._numberOfChannels);
}
// 領域確保
this._pcmData = new Array(this._wavFileInfo._numberOfChannels);
for (
let channelCount = 0;
channelCount < this._wavFileInfo._numberOfChannels;
channelCount++
) {
this._pcmData[channelCount] = new Float32Array(
this._wavFileInfo._samplesPerChannel
);
}
// 波形データ取得
for (
let sampleCount = 0;
sampleCount < this._wavFileInfo._samplesPerChannel;
sampleCount++
) {
for (
let channelCount = 0;
channelCount < this._wavFileInfo._numberOfChannels;
channelCount++
) {
this._pcmData[channelCount][sampleCount] = this.getPcmSample();
}
}
ret = true;
} catch (e) {
console.log(e);
}
})();
return ret;
}
public getPcmSample(): number {
let pcm32;
// 32ビット幅に拡張してから-11の範囲に丸める
switch (this._wavFileInfo._bitsPerSample) {
case 8:
pcm32 = this._byteReader.get8() - 128;
pcm32 <<= 24;
break;
case 16:
pcm32 = this._byteReader.get16LittleEndian() << 16;
break;
case 24:
pcm32 = this._byteReader.get24LittleEndian() << 8;
break;
default:
// 対応していないビット幅
pcm32 = 0;
break;
}
return pcm32 / 2147483647; //Number.MAX_VALUE;
}
public releasePcmData(): void {
for (
let channelCount = 0;
channelCount < this._wavFileInfo._numberOfChannels;
channelCount++
) {
delete this._pcmData[channelCount];
}
delete this._pcmData;
this._pcmData = null;
}
constructor() {
this._pcmData = null;
this._userTimeSeconds = 0.0;
this._lastRms = 0.0;
this._sampleOffset = 0.0;
this._wavFileInfo = new WavFileInfo();
this._byteReader = new ByteReader();
}
_pcmData: Array<Float32Array>;
_userTimeSeconds: number;
_lastRms: number;
_sampleOffset: number;
_wavFileInfo: WavFileInfo;
_byteReader: ByteReader;
_loadFiletoBytes = (arrayBuffer: ArrayBuffer, length: number): void => {
this._byteReader._fileByte = arrayBuffer;
this._byteReader._fileDataView = new DataView(this._byteReader._fileByte);
this._byteReader._fileSize = length;
};
}
export class WavFileInfo {
constructor() {
this._fileName = '';
this._numberOfChannels = 0;
this._bitsPerSample = 0;
this._samplingRate = 0;
this._samplesPerChannel = 0;
}
_fileName: string; ///< ファイル名
_numberOfChannels: number; ///< チャンネル数
_bitsPerSample: number; ///< サンプルあたりビット数
_samplingRate: number; ///< サンプリングレート
_samplesPerChannel: number; ///< 1チャンネルあたり総サンプル数
}
export class ByteReader {
constructor() {
this._fileByte = null;
this._fileDataView = null;
this._fileSize = 0;
this._readOffset = 0;
}
/**
* @brief 8
* @return Csm::csmUint8 8
*/
public get8(): number {
const ret = this._fileDataView.getUint8(this._readOffset);
this._readOffset++;
return ret;
}
/**
* @brief 16
* @return Csm::csmUint16 16
*/
public get16LittleEndian(): number {
const ret =
(this._fileDataView.getUint8(this._readOffset + 1) << 8) |
this._fileDataView.getUint8(this._readOffset);
this._readOffset += 2;
return ret;
}
/**
* @brief 24
* @return Csm::csmUint32 2424
*/
public get24LittleEndian(): number {
const ret =
(this._fileDataView.getUint8(this._readOffset + 2) << 16) |
(this._fileDataView.getUint8(this._readOffset + 1) << 8) |
this._fileDataView.getUint8(this._readOffset);
this._readOffset += 3;
return ret;
}
/**
* @brief 32
* @return Csm::csmUint32 32
*/
public get32LittleEndian(): number {
const ret =
(this._fileDataView.getUint8(this._readOffset + 3) << 24) |
(this._fileDataView.getUint8(this._readOffset + 2) << 16) |
(this._fileDataView.getUint8(this._readOffset + 1) << 8) |
this._fileDataView.getUint8(this._readOffset);
this._readOffset += 4;
return ret;
}
/**
* @brief
* @param[in] reference
* @retval true
* @retval false
*/
public getCheckSignature(reference: string): boolean {
const getSignature: Uint8Array = new Uint8Array(4);
const referenceString: Uint8Array = new TextEncoder().encode(reference);
if (reference.length != 4) {
return false;
}
for (let signatureOffset = 0; signatureOffset < 4; signatureOffset++) {
getSignature[signatureOffset] = this.get8();
}
return (
getSignature[0] == referenceString[0] &&
getSignature[1] == referenceString[1] &&
getSignature[2] == referenceString[2] &&
getSignature[3] == referenceString[3]
);
}
_fileByte: ArrayBuffer; ///< ロードしたファイルのバイト列
_fileDataView: DataView;
_fileSize: number; ///< ファイルサイズ
_readOffset: number; ///< ファイル参照位置
}