live2d-motionsync
Version:
Live2D Motionsync, a lip-sync library for Live2D models.
45 lines (37 loc) • 4.62 kB
JavaScript
"use strict";var h=Object.defineProperty;var d=(s,t,e)=>t in s?h(s,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):s[t]=e;var o=(s,t,e)=>d(s,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./fallback.motionsync3-lM6C-7qD.cjs"),f=`/**
* 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.
*/
class LAppAudioWorkletProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.useChannel = 0;
}
process(inputs, outputs, parameters) {
const channel = this.useChannel % inputs[0].length;
const input = inputs[0][channel];
if (input == undefined || input == null) {
return true;
}
// 後ろに追加する
const audioBuffer = Float32Array.from([...input]);
this.port.postMessage({
eventType: "data",
audioBuffer: audioBuffer,
});
let inputArray = inputs[0];
let output = outputs[0];
for (let currentChannel = 0; currentChannel < inputArray.length; ++currentChannel) {
let inputChannel = inputArray[currentChannel];
let outputChannel = output[currentChannel];
for (let i = 0; i < inputChannel.length; ++i){
outputChannel[i] = inputChannel[i];
}
}
return true;
}
}
registerProcessor('lappaudioworkletprocessor', LAppAudioWorkletProcessor);
`,a=48e3;class p{constructor(t){o(this,"_motionSync",null);o(this,"_internalModel");o(this,"_model");o(this,"_context",null);o(this,"_source",null);o(this,"_buffer",null);this._internalModel=t,this._model=t.coreModel,r.CubismMotionSync.startUp(new r.MotionSyncOption),r.CubismMotionSync.initialize()}async play(t){(this._source||this._context)&&this.reset();const e=t.getAudioTracks();if(e.length==0){r.CubismLogError("没有找到音频轨道.");return}const n=48e3,i=30,c=2;this._buffer=new _(Math.trunc(n/i*c)),this._context=new AudioContext({sampleRate:n}),this._source=this._context.createMediaStreamSource(new MediaStream([e[0]]));const l=URL.createObjectURL(new Blob([f],{type:"application/javascript"}));await this._context.audioWorklet.addModule(l);const u=new AudioWorkletNode(this._context,"lappaudioworkletprocessor");this._source.connect(u),u.port.onmessage=this.onMessage.bind(this)}async reset(){this._source&&(this._source.disconnect(),this._source=null),this._context&&(this._context.close(),this._context=null),this._buffer=null,this.resetMouthStatus()}resetMouthStatus(){try{if(!this._motionSync)return;const t=this._motionSync.getData().getSetting(0);if(!t)return;const e=t.cubismParameterList;if(!e)return;const n=e._ptr.map(i=>i.parameterIndex);for(const i of n)this._model.setParameterValueByIndex(i,0)}catch(t){console.error(t)}}pop(){if(!this._buffer)return;const t=this._buffer.toVector();return this._buffer.clear(),t}onMessage(t){const e=t.data;if(e.eventType==="data"&&e.audioBuffer)for(let n=0;n<e.audioBuffer.length;n++)this._buffer.addLast(e.audioBuffer[n])}updateMotionSync(){const t=performance.now()/1e3,e=this.pop();e&&(this._motionSync.setSoundBuffer(0,e,0),this._motionSync.updateParameters(this._model,t))}modelUpdateWithMotionSync(){if(!this._motionSync)return;const e=this._internalModel,n=e.motionManager.update;e.motionManager.update=(...i)=>{n.apply(this._internalModel.motionManager,i),this.updateMotionSync()}}loadMotionSync(t,e=a){if(t==null||t.byteLength==0){console.warn("Failed to loadMotionSync().");return}this._motionSync=r.CubismMotionSync.create(this._model,t,t.byteLength,e),this.modelUpdateWithMotionSync()}async loadDefaultMotionSync(t=a){const n=await new Blob([r.fallbackMotionsync3],{type:"application/json"}).arrayBuffer();this.loadMotionSync(n,t)}async loadMotionSyncFromUrl(t,e=a){try{const i=await(await fetch(t)).arrayBuffer();this.loadMotionSync(i,e)}catch{console.warn("Failed to loadMotionSync(). Use default fallback."),await this.loadDefaultMotionSync(e)}}}class _{constructor(t){o(this,"_buffer");o(this,"_size");o(this,"_head");this._buffer=new Float32Array(t),this._size=0,this._head=0}get size(){return this._size}addLast(t){this._buffer[this._head]=t,this._size=Math.min(this._size+1,this._buffer.length),this._head++,this._head>=this._buffer.length&&(this._head=0)}toVector(){const t=new r.csmVector(this._size);let e=this._head-this._size;e<0&&(e+=this._buffer.length);for(let n=0;n<this._size;n++)t.pushBack(this._buffer[e]),e++,e>=this._buffer.length&&(e=0);return t}clear(){this._size=0,this._head=0}}exports.MotionSync=p;