diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index aca2444..ce52e0c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,9 +18,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: ${{github.ref_name}} submodules: true - fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository. - uses: actions/setup-node@v3 with: diff --git a/README.md b/README.md index e179761..bec07b3 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Live2D models on a high level without the need to learn how the internal system #### Requirements -- PixiJS: 6.x +- PixiJS: 7.x - Cubism core: 2.1 or 4 - Browser: WebGL, ES6 @@ -108,7 +108,7 @@ import { Live2DModel } from 'pixi-live2d-display/cubism4'; - + @@ -172,7 +172,7 @@ window.PIXI = PIXI; * Time to code ```js var category_name = "Idle" // name of the morion category -var animation_index = 0 // index of animation under that motion category +var animation_index = 0 // index of animation under that motion category [null => random] var priority_number = 3 // if you want to keep the current animation going or move to new animation by force [0: no priority, 1: idle, 2: normal, 3: forced] var audio_link = "https://cdn.jsdelivr.net/gh/RaSan147/pixi-live2d-display@v1.0.3/playground/test.mp3" //[Optional arg, can be null or empty] [relative or full url path] [mp3 or wav file] var volume = 1; //[Optional arg, can be null or empty] [0.0 - 1.0] diff --git a/src/Live2DModel.ts b/src/Live2DModel.ts index c2473cf..30bce1f 100644 --- a/src/Live2DModel.ts +++ b/src/Live2DModel.ts @@ -1,5 +1,4 @@ -import type { InternalModel, ModelSettings } from "@/cubism-common"; -import { MotionPriority } from "@/cubism-common"; +import type { InternalModel, ModelSettings, MotionPriority } from "@/cubism-common"; import type { MotionManagerOptions } from "@/cubism-common/MotionManager"; import { VOLUME } from "@/cubism-common/SoundManager"; import type { Live2DFactoryOptions } from "@/factory/Live2DFactory"; @@ -168,8 +167,8 @@ export class Live2DModel extends Conta */ motion( group: string, - index: number, - priority: MotionPriority = MotionPriority.NORMAL, + index?: number, + priority?: MotionPriority, { sound = undefined, volume = VOLUME, diff --git a/src/cubism-common/MotionManager.ts b/src/cubism-common/MotionManager.ts index a06ba04..825d784 100644 --- a/src/cubism-common/MotionManager.ts +++ b/src/cubism-common/MotionManager.ts @@ -302,7 +302,7 @@ export abstract class MotionManager extends util } if (audio) { - let playSuccess = false; + let playSuccess = true; const readyToPlay = SoundManager.play(audio).catch((e) => { logger.warn(this.tag, "Failed to play audio", audio!.src, e); playSuccess = false; @@ -361,17 +361,16 @@ export abstract class MotionManager extends util crossOrigin?: string; } = {}, ): Promise { + if (!this.state.reserve(group, index, priority)) { + return false; + } // Does not start a new motion if audio is still playing if (this.currentAudio) { - if (!this.currentAudio.ended) { + if (!this.currentAudio.ended && priority != MotionPriority.FORCE) { return false; } } - if (!this.state.reserve(group, index, priority)) { - return false; - } - const definition = this.definitions[group]?.[index]; if (!definition) { @@ -387,59 +386,58 @@ export abstract class MotionManager extends util let analyzer: AnalyserNode | undefined; let context: AudioContext | undefined; - if (config.sound) { - const isBase64Content = sound && sound.startsWith("data:audio/wav;base64"); - if (sound && !isBase64Content) { - const A = document.createElement("a"); - A.href = sound; - sound = A.href; // This should be the absolute url - // since resolveURL is not working for some reason - } - const isUrlPath = sound && (sound.startsWith("http") || sound.startsWith("blob")); - const soundURL = this.getSoundFile(definition); - let file = soundURL; + let soundURL: string | undefined; + const isBase64Content = sound && sound.startsWith("data:"); + + if (sound && !isBase64Content) { + const A = document.createElement("a"); + A.href = sound; + sound = A.href; // This should be the absolute url + // since resolveURL is not working for some reason + soundURL = sound; + } else { + soundURL = this.getSoundFile(definition); if (soundURL) { - file = this.settings.resolveURL(soundURL) + "?cache-buster=" + new Date().getTime(); + soundURL = this.settings.resolveURL(soundURL); } - if (isUrlPath || isBase64Content) { - file = sound; - } - if (file) { - try { - // start to load the audio - audio = SoundManager.add( - file, - (that = this) => { - resetExpression && - expression && - that.expressionManager && - that.expressionManager.resetExpression(); - that.currentAudio = undefined; - }, // reset expression when audio is done - (e, that = this) => { - resetExpression && - expression && - that.expressionManager && - that.expressionManager.resetExpression(); - that.currentAudio = undefined; - }, // on error - crossOrigin, - ); - - this.currentAudio = audio!; - - SoundManager.volume = volume; - - // Add context - context = SoundManager.addContext(this.currentAudio); - this.currentContext = context; - - // Add analyzer - analyzer = SoundManager.addAnalyzer(this.currentAudio, this.currentContext); - this.currentAnalyzer = analyzer; - } catch (e) { - logger.warn(this.tag, "Failed to create audio", soundURL, e); - } + } + const file: string | undefined = soundURL; + + if (file) { + try { + // start to load the audio + audio = SoundManager.add( + file, + (that = this) => { + resetExpression && + expression && + that.expressionManager && + that.expressionManager.resetExpression(); + that.currentAudio = undefined; + }, // reset expression when audio is done + (e, that = this) => { + resetExpression && + expression && + that.expressionManager && + that.expressionManager.resetExpression(); + that.currentAudio = undefined; + }, // on error + crossOrigin, + ); + + this.currentAudio = audio!; + + SoundManager.volume = volume; + + // Add context + context = SoundManager.addContext(this.currentAudio); + this.currentContext = context; + + // Add analyzer + analyzer = SoundManager.addAnalyzer(this.currentAudio, this.currentContext); + this.currentAnalyzer = analyzer; + } catch (e) { + logger.warn(this.tag, "Failed to create audio", soundURL, e); } } diff --git a/src/cubism2/Cubism2InternalModel.ts b/src/cubism2/Cubism2InternalModel.ts index 828fcb0..862d9af 100644 --- a/src/cubism2/Cubism2InternalModel.ts +++ b/src/cubism2/Cubism2InternalModel.ts @@ -36,7 +36,7 @@ export class Cubism2InternalModel extends InternalModel { angleZParamIndex: number; bodyAngleXParamIndex: number; breathParamIndex: number; - mouthFormIndex: number; + // mouthFormIndex: number; textureFlipY = true; @@ -74,7 +74,7 @@ export class Cubism2InternalModel extends InternalModel { this.angleZParamIndex = coreModel.getParamIndex("PARAM_ANGLE_Z"); this.bodyAngleXParamIndex = coreModel.getParamIndex("PARAM_BODY_ANGLE_X"); this.breathParamIndex = coreModel.getParamIndex("PARAM_BREATH"); - this.mouthFormIndex = coreModel.getParamIndex("PARAM_MOUTH_FORM"); + // this.mouthFormIndex = coreModel.getParamIndex("PARAM_MOUTH_FORM"); this.init(); } @@ -250,11 +250,13 @@ export class Cubism2InternalModel extends InternalModel { let value = this.motionManager.mouthSync(); let min_ = 0; const max_ = 1; - const weight = 1.2; - if (value > 0) { + const bias_weight = 1.2; + const bias_power = 0.7; + if (value > 0.0) { min_ = 0.4; } - value = clamp(value * weight, min_, max_); + value = Math.pow(value, bias_power); + value = clamp(value * bias_weight, min_, max_); for (let i = 0; i < this.motionManager.lipSyncIds.length; ++i) { this.coreModel.setParamFloat( diff --git a/src/cubism4/Cubism4MotionManager.ts b/src/cubism4/Cubism4MotionManager.ts index 20192ba..8a8b66a 100644 --- a/src/cubism4/Cubism4MotionManager.ts +++ b/src/cubism4/Cubism4MotionManager.ts @@ -25,15 +25,19 @@ export class Cubism4MotionManager extends MotionManager