Skip to content

Commit

Permalink
Merge pull request #126 from lobehub/fix/i18n
Browse files Browse the repository at this point in the history
fix: dance playing error
  • Loading branch information
rdmclin2 authored Aug 5, 2024
2 parents 0c9ce18 + 0e57daa commit 94a1299
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 53 deletions.
2 changes: 1 addition & 1 deletion src/features/AgentViewer/ToolBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const ToolBar = (props: ToolBarProps) => {
break;
}
case 'power': {
viewer.model?.resetToIdle();
viewer.resetToIdle();
break;
}
case 'screenShot': {
Expand Down
16 changes: 8 additions & 8 deletions src/features/DanceList/Item/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const DanceItem = (props: DanceItemProps) => {
s.setCurrentPlayId,
]);

const [isPlaying, setIsPlaying] = useGlobalStore((s) => [s.isPlaying, s.setIsPlaying]);
const [isPlaying, setIsPlaying] = useState(false);

const isCurrentPlay = currentPlayId ? currentPlayId === danceItem.danceId : false;
const isSelected = currentIdentifier === danceItem.danceId;
Expand All @@ -45,20 +45,20 @@ const DanceItem = (props: DanceItemProps) => {
const { downloading: danceDownloading, percent: dancePercent, fetchDanceUrl } = useLoadDance();
const viewer = useGlobalStore((s) => s.viewer);

const handlePlayPause = () => {
viewer.model?.disposeAll();
const handlePlayPause = async () => {
if (isPlaying && isCurrentPlay) {
setIsPlaying(false);
viewer?.resetToIdle();
} else {
setCurrentPlayId(danceItem.danceId);
setIsPlaying(true);
const audioPromise = fetchAudioUrl(danceItem.danceId, danceItem.audio);
const dancePromise = fetchDanceUrl(danceItem.danceId, danceItem.src);
Promise.all([dancePromise, audioPromise]).then((res) => {
if (!res) return;
const [danceUrl, audioUrl] = res;
if (danceUrl && audioUrl) viewer.model?.dance(danceUrl, audioUrl);
});
const [danceUrl, audioUrl] = await Promise.all([dancePromise, audioPromise]);
if (danceUrl && audioUrl)
viewer?.dance(danceUrl, audioUrl, () => {
setIsPlaying(false);
});
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ export class AudioPlayer {
this.bufferSource = undefined;
}

public async playFromArrayBuffer(
buffer: ArrayBuffer,
onEnded?: () => void,
onProgress?: () => void,
) {
public async playFromArrayBuffer(buffer: ArrayBuffer, onEnded?: () => void) {
this.stopPlay();
const audioBuffer = await this.audio.decodeAudioData(buffer);

this.bufferSource = this.audio.createBufferSource();
Expand All @@ -22,9 +19,6 @@ export class AudioPlayer {
if (onEnded) {
this.bufferSource.addEventListener('ended', onEnded);
}
if (onProgress) {
this.bufferSource.addEventListener('time', onProgress);
}
}

public stopPlay() {
Expand Down
File renamed without changes.
37 changes: 4 additions & 33 deletions src/libs/vrmViewer/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { AnimationAction, AnimationClip } from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { LoopOnce } from 'three/src/constants';

import { AudioPlayer } from '@/features/audioPlayer/audioPlayer';
import { loadMixamoAnimation } from '@/libs/FBXAnimation/loadMixamoAnimation';
import { loadVMDAnimation } from '@/libs/VMDAnimation/loadVMDAnimation';
import { convert } from '@/libs/VMDAnimation/vmd2vrmanim';
Expand All @@ -13,6 +12,7 @@ import IKHandler from '@/libs/VMDAnimation/vrm-ik-handler';
import { VRMAnimation } from '@/libs/VRMAnimation/VRMAnimation';
import { loadVRMAnimation } from '@/libs/VRMAnimation/loadVRMAnimation';
import { VRMLookAtSmootherLoaderPlugin } from '@/libs/VRMLookAtSmootherLoaderPlugin/VRMLookAtSmootherLoaderPlugin';
import { AudioPlayer } from '@/libs/audioPlayer/audioPlayer';
import { EmoteController } from '@/libs/emoteController/emoteController';
import { LipSync } from '@/libs/lipSync/lipSync';
import { Screenplay } from '@/types/touch';
Expand All @@ -28,18 +28,15 @@ export class Model {

private _lookAtTargetParent: THREE.Object3D;
private _lipSync?: LipSync;
private _audioPlayer?: AudioPlayer;

private _action: AnimationAction | undefined;
private _clip: AnimationClip | undefined;
private _audio: string | undefined;

constructor(lookAtTargetParent: THREE.Object3D) {
this._lookAtTargetParent = lookAtTargetParent;
this._lipSync = new LipSync(new AudioContext());
this._audioPlayer = new AudioPlayer(new AudioContext());
this._action = undefined;
this._clip = undefined;
this._audio = undefined;
}

public async loadVRM(url: string): Promise<void> {
Expand Down Expand Up @@ -95,11 +92,6 @@ export class Model {
this._action.stop();
this._action = undefined;
}

if (this._audio) {
this._audioPlayer?.stopPlay();
this._audio = undefined;
}
}

/**
Expand Down Expand Up @@ -140,41 +132,20 @@ export class Model {
}
}

public async loadVMD(animationUrl: string) {
public async loadVMD(animationUrl: string, loop: boolean = true) {
const { vrm, mixer } = this;

if (vrm && mixer) {
this.disposeAll();
const clip = await loadVMDAnimation(animationUrl, vrm);
const action = mixer.clipAction(clip);
if (!loop) action.setLoop(LoopOnce, 1);
action.play();
this._action = action;
this._clip = clip;
}
}

/**
* 播放舞蹈,以音乐文件的播放作为结束标志。
*/
public async dance(danceUrl: string, audioUrl: string, onEnd?: () => void) {
const { vrm, mixer } = this;
if (vrm && mixer) {
this.disposeAll();
const clip = await loadVMDAnimation(danceUrl, vrm);
const action = mixer.clipAction(clip);
action.setLoop(LoopOnce, 1).play(); // play animation
if (audioUrl) {
this._audioPlayer?.playFromURL(audioUrl, () => {
onEnd?.();
});
this._audio = audioUrl;
}

this._action = action;
this._clip = clip;
}
}

public async resetToIdle() {
this.disposeAll();

Expand Down
44 changes: 43 additions & 1 deletion src/libs/vrmViewer/viewer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { Parser } from 'mmd-parser';
import * as THREE from 'three';
import { GridHelper, Mesh, MeshLambertMaterial, PlaneGeometry } from 'three';
import { Audio, GridHelper, Mesh, MeshLambertMaterial, PlaneGeometry } from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { LoopOnce } from 'three/src/constants';

import { loadVMDAnimation } from '@/libs/VMDAnimation/loadVMDAnimation';
import { AudioPlayer } from '@/libs/audioPlayer/audioPlayer';

import { Model } from './model';

Expand All @@ -12,6 +16,7 @@ export class Viewer {
private _renderer?: THREE.WebGLRenderer;
private _clock: THREE.Clock;
private _scene: THREE.Scene;
private _sound?: Audio;
private _cameraHelper?: THREE.CameraHelper;
private _camera?: THREE.PerspectiveCamera;
private _cameraControls?: OrbitControls;
Expand Down Expand Up @@ -45,6 +50,36 @@ export class Viewer {
this._clock.start();
}

/**
* 播放舞蹈,以音乐文件的播放作为结束标志。
*/
public async dance(danceUrl: string, audioUrl: string, onEnd?: () => void) {
if (!this._sound || !this.model) {
console.error('Audio Object or Model Object Not Existed');
return null;
}
this._sound.stop();
this.model?.disposeAll();
const audioLoader = new THREE.AudioLoader();
// 监听音频播放结束事件
this._sound.onEnded = () => {
onEnd?.();
this.model?.loadIdleAnimation();
};
const buffer = await audioLoader.loadAsync(audioUrl);
this._sound.setBuffer(buffer);
this._sound.setVolume(0.5);
this._sound.play();

this.model?.loadVMD(danceUrl, false);
}

public resetToIdle() {
this._sound?.stop();
this.model?.disposeAll();
this.model?.loadIdleAnimation();
}

/**
* 加载舞台
* @param buffer
Expand Down Expand Up @@ -111,6 +146,13 @@ export class Viewer {
this._cameraControls?.target.set(0, 0, 0);
this._cameraControls.update();

// Audio 音频播放
const listener = new THREE.AudioListener();
this._camera.add(listener);

// 创建一个全局 audio 源
this._sound = new THREE.Audio(listener);

const resizeObserver = new ResizeObserver(() => {
setTimeout(() => this.resize(), 0);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';

import { DEFAULT_MOTION_ANIMATION } from '@/constants/touch';
import { speakCharacter } from '@/features/messages/speakCharacter';
import { speakCharacter } from '@/libs/messages/speakCharacter';
import { agentSelectors, useAgentStore } from '@/store/agent';
import { useGlobalStore } from '@/store/global';
import { TouchAction } from '@/types/touch';
Expand Down
2 changes: 1 addition & 1 deletion src/services/chat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OPENAI_API_KEY, OPENAI_END_POINT } from '@/constants/openai';
import { speakCharacter } from '@/features/messages/speakCharacter';
import { speakCharacter } from '@/libs/messages/speakCharacter';
import { useGlobalStore } from '@/store/global';
import { sessionSelectors, useSessionStore } from '@/store/session';
import { configSelectors, useSettingStore } from '@/store/setting';
Expand Down

0 comments on commit 94a1299

Please sign in to comment.