Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live2D with Lipsync (using audio file/link) #122

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
99fd153
Added lip-sync module with audio
RaSan147 Oct 6, 2023
d378339
Automated report
RaSan147 Oct 6, 2023
3ff3a53
Update index.js links
RaSan147 Oct 6, 2023
8539e76
Update test.yml
RaSan147 Oct 12, 2023
cf0f4c4
Update test.yml
RaSan147 Oct 12, 2023
f53e25a
Update test.yml
RaSan147 Oct 12, 2023
7deba12
Fix Lip sync. Breaking change
RaSan147 Oct 12, 2023
06bfbd3
Automated report
RaSan147 Oct 12, 2023
1e89254
Update README.md and added video
RaSan147 Oct 12, 2023
34831e8
Merge branch 'master' into master
RaSan147 Dec 7, 2023
2d0c512
Fix type error on ci test
RaSan147 Dec 7, 2023
2617eae
clear dist, using workflow to get output files
RaSan147 Dec 7, 2023
de764a1
removed Version number
RaSan147 Dec 7, 2023
2877f47
Now supports other b64 audio
RaSan147 Dec 7, 2023
25ddef6
Remove audio url validation
RaSan147 Dec 7, 2023
b4a394a
rename speakUp -> speak
RaSan147 Dec 7, 2023
6f3a779
rename resetMotions -> stopMotions
RaSan147 Dec 7, 2023
e878d79
return false when audio play fails
RaSan147 Dec 7, 2023
031ca86
Added crossorigin for speak voice source
RaSan147 Dec 13, 2023
ae1c923
Place missing CrossOrigin arg
RaSan147 Dec 13, 2023
c0cb7f5
Update MotionManager.ts
RaSan147 Dec 14, 2023
c8eca3f
Update readme as per PR
RaSan147 Dec 14, 2023
7f8ec7a
Check any base64 data for audio
RaSan147 Dec 14, 2023
06d7255
Remove autoplay attr from audio
RaSan147 Dec 14, 2023
96a6f82
Remove cache blocker.
RaSan147 Dec 14, 2023
9fc5a21
remove wav only blob condition
RaSan147 Dec 30, 2023
b005f73
Merge branch 'guansss:master' into for_PR
RaSan147 Dec 30, 2023
ff1a588
RAN `npm run lint:fix`
RaSan147 Dec 30, 2023
0495f5b
Merge branch 'for_PR' of https://github.com/RaSan147/pixi-live2d-disp…
RaSan147 Dec 30, 2023
1cd47d6
fix startmotion, getting error
RaSan147 Feb 9, 2024
65f31e2
All test passed. Everything OK
RaSan147 Feb 9, 2024
92af59b
revert: undo unexpected changes
guansss Feb 28, 2024
b00b64b
fix: model.speak() not returning true on success
guansss Feb 28, 2024
b511d74
feat: extract lip sync code to LipSync class
guansss Mar 18, 2024
12e9530
test: fix broken test procedure
guansss Mar 18, 2024
ee42229
test: add lip sync test
guansss Mar 18, 2024
6f3cd68
chore: fix failing npm install
guansss Mar 18, 2024
40cd3f3
feat: skip lip sync analyzer when no audio is playing
guansss Apr 1, 2024
638053e
fix: model.motion() parameters should be optional
guansss Apr 8, 2024
9a9d20f
feat: default lip sync id for cubism 4 models
guansss Apr 8, 2024
2ecdb53
feat: make options.crossOrigin also work for audios
guansss Apr 8, 2024
9cb5c38
test: add aborted lip sync test
guansss Apr 8, 2024
f53509b
test: make lip sync test more stable
guansss Apr 14, 2024
d69a771
test: make lip sync test more stable (for real)
guansss Apr 14, 2024
94a7ea1
feat: merge lipSync.analyze() into lipSync.getValue()
guansss Apr 15, 2024
87476a4
test: temporarily disable parallelism to fix random failures
guansss Apr 15, 2024
718215d
revert: remove updateFacialEmotion method
guansss Apr 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true

Expand Down Expand Up @@ -57,3 +57,9 @@ jobs:
name: diff-images
path: test/**/__image_snapshots__/__diff_output__/*
if-no-files-found: ignore


- uses: actions/upload-artifact@v3
with:
name: my-artifact
path: ${{ github.workspace }}/dist
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ core
/build
/*.build
/lib
/dist
/types
/site
/types
/dist

.DS_Store
node_modules
Expand Down
7 changes: 6 additions & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ Or run the tests and update snapshots:
npm run test:u
```

If you get an error like `This version of ChromeDriver only supports Chrome version XX`, you need to either upgrade or downgrade your Chrome browser to match that version, or run `npm install chromedriver@<version>` to install the correct version of ChromeDriver (don't commit this change if you are contributing to this project!).
If you get an error like `This version of ChromeDriver only supports Chrome version XX`, you need to do either of the following:

- Check if your Chrome browser has a "Relaunch to update" button in the top right corner. If it does, click the button to update Chrome to the latest version.
- Delete the installed `chromedriver` folder (for example `C:\Users\XXX\AppData\Local\Temp\chromedriver\win64-120.0.6099.109\chromedriver-win64` on Windows).

If you get an error like `Error: spawn C:\Users\XXX\AppData\Local\Temp\chromedriver\win64-120.0.6099.109\chromedriver-win64\chromedriver.exe ENOENT`, you need to delete the `chromedriver` folder just like the step 2 above.

## Playground

Expand Down
128 changes: 106 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ Live2D models on a high level without the need to learn how the internal system
- Enhanced motion reserving logic compared to the official framework
- Loading from uploaded files / zip files (experimental)
- Fully typed - we all love types!
- Live Lipsync

#### Requirements

- PixiJS: 6.x
- PixiJS: 7.x
- Cubism core: 2.1 or 4
- Browser: WebGL, ES6

Expand Down Expand Up @@ -97,9 +98,17 @@ import { Live2DModel } from 'pixi-live2d-display/cubism2';
import { Live2DModel } from 'pixi-live2d-display/cubism4';
```

#### Via CDN
#### Via CDN (lipsync patched)

```html

<!-- Load Cubism and PixiJS -->
<script src="https://cubism.live2d.com/sdk-web/cubismcore/live2dcubismcore.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/dylanNew/live2d/webgl/Live2D/lib/live2d.min.js"></script>
<script src="https://pixijs.download/v7.4.0/pixi.min.js"></script>


<!-- if support for both Cubism 2.1 and 4 -->
<script src="https://cdn.jsdelivr.net/npm/pixi-live2d-display/dist/index.min.js"></script>

<!-- if only Cubism 2.1 -->
Expand All @@ -122,31 +131,106 @@ import { Live2DModel } from 'pixi-live2d-display';
window.PIXI = PIXI;

(async function () {
const app = new PIXI.Application({
view: document.getElementById('canvas'),
});
const app = new PIXI.Application({
view: document.getElementById('canvas'),
});

const model = await Live2DModel.from('shizuku.model.json');

app.stage.addChild(model);

// transforms
model.x = 100;
model.y = 100;
model.rotation = Math.PI;
model.skew.x = Math.PI;
model.scale.set(2, 2);
model.anchor.set(0.5, 0.5);

// interaction
model.on('hit', (hitAreas) => {
if (hitAreas.includes('body')) {
model.motion('tap_body');
}
});
})();
```

const model = await Live2DModel.from('shizuku.model.json');
## Do some motion manually
* First either you need to load your model on Live2d viewer app, or the Website by guansss [here](https://guansss.github.io/live2d-viewer-web/)
* Check for motion category names (like "idle", "" (blank) etc)
* Screenshot will be added soon
* Under those motion categories, each motions are used by their index
* Motion Priority table:
* 0: no priority
* 1: maybe [for idle animation]
* 2: normal [default when normal action]
* 3: Just do it! Do id now! [Forced] [default when using audio]
* Time to code
```js
var category_name = "Idle" // name of the morion 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://audio-samples.github.io/samples/mp3/blizzard_primed/sample-4.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]
var expression = 4; //[Optional arg, can be null or empty] [index|name of expression]
var resetExpression = true; //[Optional arg, can be null or empty] [true|false] [default: true] [if true, expression will be reset to default after animation is over]

model.motion(category_name, animation_index, priority_number, {sound: audio_link, volume: volume, expression:expression, resetExpression:resetExpression})
// Note: during this animation with sound, other animation will be ignored, even its forced. Once over, it'll be back to normal

// if you dont want voice, just ignore the option
model.motion(category_name, animation_index, priority_number)
model.motion(category_name, animation_index, priority_number, {expression:expression, resetExpression:resetExpression})
model.motion(category_name, animation_index, priority_number, {expression:expression, resetExpression:false})

app.stage.addChild(model);
```

// transforms
model.x = 100;
model.y = 100;
model.rotation = Math.PI;
model.skew.x = Math.PI;
model.scale.set(2, 2);
model.anchor.set(0.5, 0.5);

// interaction
model.on('hit', (hitAreas) => {
if (hitAreas.includes('body')) {
model.motion('tap_body');
}
});
})();
## Lipsync Only
* You can do sound lipsync even without triggering any motion
* This supports expressions arg too (if you have/need any)
* Demo code
```js
var audio_link = "https://audio-samples.github.io/samples/mp3/blizzard_primed/sample-4.mp3" // [relative or full url path] [mp3 or wav file]
var volume = 1; // [Optional arg, can be null or empty] [0.0 - 1.0]
var expression = 4; // [Optional arg, can be null or empty] [index|name of expression]
var resetExpression = true; // [Optional arg, can be null or empty] [true|false] [default: true] [if true, expression will be reset to default after animation is over]

model.speak(audio_link, {volume: volume, expression:expression, resetExpression:resetExpression})

// Or if you want to keep some things default
model.speak(audio_link)
model.speak(audio_link, {volume: volume})
model.speak(audio_link, {expression:expression, resetExpression:resetExpression})

```

## Suddenly stop audio and lipsync
* Demo code
```js
model.stopSpeaking()
```

## Reset motions as well as audio and lipsync
* Demo code
```js
model.stopMotions()
```

## Totally destroy the model
* This will also stop the motion and audio from running and hide the model
* Demo code
```js
model.destroy()
```

## Result
https://user-images.githubusercontent.com/34002411/230723497-612146b1-5593-4dfa-911d-accb331c5b9b.mp4

# See here for more Documentation: [Documentation](https://guansss.github.io/pixi-live2d-display/)



## Package importing

When importing Pixi packages on-demand, you may need to manually register some plugins to enable optional features.
Expand Down
Loading
Loading