pixi-live2d-display-advanced 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,6 +23,7 @@ var __async = (__this, __arguments, generator) => {
23
23
  });
24
24
  };
25
25
  import { utils as utils$1, Matrix, Texture, Transform, Point, ObservablePoint } from "@pixi/core";
26
+ import { Sound, webaudio } from "@pixi/sound";
26
27
  import { Container } from "@pixi/display";
27
28
  const LOGICAL_WIDTH = 2;
28
29
  const LOGICAL_HEIGHT = 2;
@@ -77,7 +78,7 @@ const config = {
77
78
  preserveExpressionOnMotion: true,
78
79
  cubism4: CubismConfig
79
80
  };
80
- const VERSION = "v0.4.0";
81
+ const VERSION = "v0.5.0";
81
82
  const logger = {
82
83
  log(tag, ...messages) {
83
84
  if (config.logLevel <= config.LOG_LEVEL_VERBOSE) {
@@ -3711,58 +3712,55 @@ class SoundManager {
3711
3712
  * @param file - URL of the sound file.
3712
3713
  * @param onFinish - Callback invoked when the playback has finished.
3713
3714
  * @param onError - Callback invoked when error occurs.
3714
- * @param crossOrigin - Cross origin setting.
3715
3715
  * @return Created audio element.
3716
3716
  */
3717
- static add(file, onFinish, onError, crossOrigin) {
3718
- const audio = new Audio(file);
3719
- audio.volume = this._volume;
3720
- audio.preload = "auto";
3721
- audio.crossOrigin = crossOrigin;
3722
- audio.addEventListener("ended", () => {
3723
- this.dispose(audio);
3724
- onFinish == null ? void 0 : onFinish();
3725
- });
3726
- audio.addEventListener("error", (e) => {
3727
- this.dispose(audio);
3728
- logger.warn(TAG$2, `Error occurred on "${file}"`, e.error);
3729
- onError == null ? void 0 : onError(e.error);
3717
+ static add(file, onFinish, onError) {
3718
+ return __async(this, null, function* () {
3719
+ try {
3720
+ const task = new Promise((resolve, reject) => {
3721
+ const audio = Sound.from({
3722
+ url: file,
3723
+ volume: this._volume,
3724
+ preload: true,
3725
+ complete: () => {
3726
+ this.dispose(audio);
3727
+ onFinish == null ? void 0 : onFinish();
3728
+ },
3729
+ loaded: () => {
3730
+ if (!(audio.media instanceof webaudio.WebAudioMedia)) {
3731
+ reject(new Error(`Error: ${file} is not WebAudioMedia`));
3732
+ }
3733
+ debugger;
3734
+ resolve(audio);
3735
+ }
3736
+ });
3737
+ });
3738
+ return yield task;
3739
+ } catch (e) {
3740
+ logger.warn(TAG$2, `Error occurred on "${file}"`, e);
3741
+ onError == null ? void 0 : onError(e);
3742
+ return null;
3743
+ }
3730
3744
  });
3731
- this.audios.push(audio);
3732
- return audio;
3733
3745
  }
3734
3746
  /**
3735
3747
  * Plays the sound.
3736
3748
  * @param audio - An audio element.
3737
- * @return Promise that resolves when the audio is ready to play, rejects when error occurs.
3738
3749
  */
3739
3750
  static play(audio) {
3740
- return new Promise((resolve, reject) => {
3741
- var _a;
3742
- (_a = audio.play()) == null ? void 0 : _a.catch((e) => {
3743
- audio.dispatchEvent(new ErrorEvent("error", { error: e }));
3744
- reject(e);
3745
- });
3746
- if (audio.readyState === audio.HAVE_ENOUGH_DATA) {
3747
- resolve();
3748
- } else {
3749
- audio.addEventListener("canplaythrough", resolve);
3750
- }
3751
- });
3752
- }
3753
- static addContext(audio) {
3754
- const context = new AudioContext();
3755
- this.contexts.push(context);
3756
- return context;
3751
+ audio.play();
3757
3752
  }
3758
3753
  static addAnalyzer(audio, context) {
3759
- const source = context.createMediaElementSource(audio);
3754
+ const media = audio.media;
3755
+ const source = context.createBufferSource();
3756
+ source.buffer = media.buffer;
3760
3757
  const analyser = context.createAnalyser();
3761
3758
  analyser.fftSize = config.fftSize;
3762
3759
  analyser.minDecibels = -90;
3763
3760
  analyser.maxDecibels = -10;
3764
3761
  analyser.smoothingTimeConstant = 0.85;
3765
3762
  source.connect(analyser);
3763
+ source.start(0);
3766
3764
  analyser.connect(context.destination);
3767
3765
  this.analysers.push(analyser);
3768
3766
  return analyser;
@@ -3773,17 +3771,21 @@ class SoundManager {
3773
3771
  * @return Returns value to feed into lip sync
3774
3772
  */
3775
3773
  static analyze(analyser) {
3776
- if (analyser != void 0) {
3777
- const pcmData = new Float32Array(analyser.fftSize);
3778
- let sumSquares = 0;
3779
- analyser.getFloatTimeDomainData(pcmData);
3780
- for (const amplitude of pcmData) {
3781
- sumSquares += amplitude * amplitude;
3782
- }
3783
- return parseFloat(Math.sqrt(sumSquares / pcmData.length * 20).toFixed(1));
3784
- } else {
3785
- return parseFloat(Math.random().toFixed(1));
3786
- }
3774
+ if (!analyser) return parseFloat(Math.random().toFixed(1));
3775
+ const buffer = new Float32Array(analyser.fftSize);
3776
+ analyser.getFloatTimeDomainData(buffer);
3777
+ let sumSquares = 0;
3778
+ for (let i = 0; i < buffer.length; i++) {
3779
+ sumSquares += __pow(buffer[i], 2);
3780
+ }
3781
+ const rms = Math.sqrt(sumSquares / buffer.length);
3782
+ const minDecibel = -100;
3783
+ const db = 20 * Math.log10(rms || __pow(10, minDecibel / 20));
3784
+ const scaledDb = Math.min(
3785
+ Math.max((db - analyser.minDecibels) / (analyser.maxDecibels - analyser.minDecibels), 0),
3786
+ 1
3787
+ );
3788
+ return parseFloat(scaledDb.toFixed(1));
3787
3789
  }
3788
3790
  /**
3789
3791
  * Disposes an audio element and removes it from {@link audios}.
@@ -3791,7 +3793,6 @@ class SoundManager {
3791
3793
  */
3792
3794
  static dispose(audio) {
3793
3795
  audio.pause();
3794
- audio.removeAttribute("src");
3795
3796
  remove(this.audios, audio);
3796
3797
  }
3797
3798
  /**
@@ -3799,7 +3800,7 @@ class SoundManager {
3799
3800
  */
3800
3801
  static destroy() {
3801
3802
  for (let i = this.contexts.length - 1; i >= 0; i--) {
3802
- this.contexts[i].close();
3803
+ setTimeout(() => this.contexts[i].close());
3803
3804
  }
3804
3805
  for (let i = this.audios.length - 1; i >= 0; i--) {
3805
3806
  this.dispose(this.audios[i]);
@@ -3851,7 +3852,7 @@ class MotionManager extends utils$1.EventEmitter {
3851
3852
  __publicField(this, "state", new MotionState());
3852
3853
  /**
3853
3854
  * Audio element of the current motion if a sound file is defined with it.
3854
- * @type {HTMLAudioElement | undefined}
3855
+ * @type {Sound | undefined}
3855
3856
  */
3856
3857
  __publicField(this, "currentAudio");
3857
3858
  /**
@@ -3966,13 +3967,13 @@ class MotionManager extends utils$1.EventEmitter {
3966
3967
  }
3967
3968
  /**
3968
3969
  * Initializes audio playback and sets up audio analysis for lipsync.
3969
- * @param audio - The HTMLAudioElement to initialize.
3970
+ * @param audio - The Sound to initialize.
3970
3971
  * @param volume - The playback volume (0-1).
3971
3972
  */
3972
3973
  initializeAudio(audio, volume) {
3973
3974
  this.currentAudio = audio;
3974
3975
  SoundManager.volume = volume;
3975
- this.currentContext = SoundManager.addContext(this.currentAudio);
3976
+ this.currentContext = audio.context.audioContext;
3976
3977
  this.currentAnalyzer = SoundManager.addAnalyzer(this.currentAudio, this.currentContext);
3977
3978
  }
3978
3979
  /**
@@ -3991,7 +3992,6 @@ class MotionManager extends utils$1.EventEmitter {
3991
3992
  volume = VOLUME,
3992
3993
  expression,
3993
3994
  resetExpression = true,
3994
- crossOrigin,
3995
3995
  onFinish,
3996
3996
  onError
3997
3997
  } = {}) {
@@ -4000,7 +4000,7 @@ class MotionManager extends utils$1.EventEmitter {
4000
4000
  }
4001
4001
  let audio;
4002
4002
  if (this.currentAudio) {
4003
- if (!this.currentAudio.ended) {
4003
+ if (this.currentAudio.isPlaying) {
4004
4004
  return false;
4005
4005
  }
4006
4006
  }
@@ -4017,7 +4017,7 @@ class MotionManager extends utils$1.EventEmitter {
4017
4017
  const file = sound;
4018
4018
  if (file) {
4019
4019
  try {
4020
- audio = SoundManager.add(
4020
+ audio = yield SoundManager.add(
4021
4021
  file,
4022
4022
  (that = this) => {
4023
4023
  logger.warn(this.tag, "Audio finished playing");
@@ -4027,7 +4027,6 @@ class MotionManager extends utils$1.EventEmitter {
4027
4027
  }
4028
4028
  that.currentAudio = void 0;
4029
4029
  },
4030
- // reset expression when audio is done
4031
4030
  (e, that = this) => {
4032
4031
  logger.error(this.tag, "Error during audio playback:", e);
4033
4032
  onError == null ? void 0 : onError(e);
@@ -4035,9 +4034,7 @@ class MotionManager extends utils$1.EventEmitter {
4035
4034
  that.expressionManager.resetExpression();
4036
4035
  }
4037
4036
  that.currentAudio = void 0;
4038
- },
4039
- // on error
4040
- crossOrigin
4037
+ }
4041
4038
  );
4042
4039
  this.initializeAudio(audio, volume);
4043
4040
  } catch (e) {
@@ -4047,15 +4044,16 @@ class MotionManager extends utils$1.EventEmitter {
4047
4044
  }
4048
4045
  if (audio) {
4049
4046
  let playSuccess = true;
4050
- const readyToPlay = SoundManager.play(audio).catch((e) => {
4051
- logger.warn(this.tag, "Failed to play audio", audio.src, e);
4052
- playSuccess = false;
4053
- });
4054
- if (config.motionSync) {
4055
- yield readyToPlay;
4056
- if (!playSuccess) {
4057
- return false;
4047
+ try {
4048
+ if (config.motionSync) {
4049
+ SoundManager.play(audio);
4058
4050
  }
4051
+ } catch (e) {
4052
+ logger.warn(this.tag, "Failed to play audio", audio.url, e);
4053
+ playSuccess = false;
4054
+ }
4055
+ if (!playSuccess) {
4056
+ return false;
4059
4057
  }
4060
4058
  }
4061
4059
  if (this.state.shouldOverrideExpression()) {
@@ -4089,7 +4087,6 @@ class MotionManager extends utils$1.EventEmitter {
4089
4087
  volume = VOLUME,
4090
4088
  expression = void 0,
4091
4089
  resetExpression = true,
4092
- crossOrigin,
4093
4090
  onFinish,
4094
4091
  onError,
4095
4092
  ignoreParamIds = []
@@ -4099,7 +4096,7 @@ class MotionManager extends utils$1.EventEmitter {
4099
4096
  return false;
4100
4097
  }
4101
4098
  if (this.currentAudio) {
4102
- if (!this.currentAudio.ended && priority != MotionPriority.FORCE) {
4099
+ if (this.currentAudio.isPlaying && priority != MotionPriority.FORCE) {
4103
4100
  return false;
4104
4101
  }
4105
4102
  }
@@ -4127,7 +4124,7 @@ class MotionManager extends utils$1.EventEmitter {
4127
4124
  const file = soundURL;
4128
4125
  if (file) {
4129
4126
  try {
4130
- audio = SoundManager.add(
4127
+ audio = yield SoundManager.add(
4131
4128
  file,
4132
4129
  (that = this) => {
4133
4130
  onFinish == null ? void 0 : onFinish();
@@ -4136,7 +4133,6 @@ class MotionManager extends utils$1.EventEmitter {
4136
4133
  }
4137
4134
  that.currentAudio = void 0;
4138
4135
  },
4139
- // reset expression when audio is done
4140
4136
  (e, that = this) => {
4141
4137
  logger.error(this.tag, "Error during audio playback:", e);
4142
4138
  onError == null ? void 0 : onError(e);
@@ -4144,9 +4140,7 @@ class MotionManager extends utils$1.EventEmitter {
4144
4140
  that.expressionManager.resetExpression();
4145
4141
  }
4146
4142
  that.currentAudio = void 0;
4147
- },
4148
- // on error
4149
- crossOrigin
4143
+ }
4150
4144
  );
4151
4145
  this.initializeAudio(audio, volume);
4152
4146
  } catch (e) {
@@ -4155,11 +4149,12 @@ class MotionManager extends utils$1.EventEmitter {
4155
4149
  }
4156
4150
  const motion = yield this.loadMotion(group, index);
4157
4151
  if (audio) {
4158
- const readyToPlay = SoundManager.play(audio).catch(
4159
- (e) => logger.warn(this.tag, "Failed to play audio", audio.src, e)
4160
- );
4161
4152
  if (config.motionSync) {
4162
- yield readyToPlay;
4153
+ try {
4154
+ SoundManager.play(audio);
4155
+ } catch (e) {
4156
+ logger.warn(this.tag, "Failed to play audio", audio.url, e);
4157
+ }
4163
4158
  }
4164
4159
  }
4165
4160
  if (!this.state.start(motion, group, index, priority)) {
@@ -4201,7 +4196,6 @@ class MotionManager extends utils$1.EventEmitter {
4201
4196
  volume = VOLUME,
4202
4197
  expression,
4203
4198
  resetExpression = true,
4204
- crossOrigin,
4205
4199
  onFinish,
4206
4200
  onError
4207
4201
  } = {}) {
@@ -4220,7 +4214,6 @@ class MotionManager extends utils$1.EventEmitter {
4220
4214
  volume,
4221
4215
  expression,
4222
4216
  resetExpression,
4223
- crossOrigin,
4224
4217
  onFinish,
4225
4218
  onError
4226
4219
  });
@@ -5532,7 +5525,6 @@ class Live2DModel extends Container {
5532
5525
  * @param [options.volume=0.5] - Volume of the sound (0-1).
5533
5526
  * @param [options.expression] - In case you want to mix up an expression while playing sound (bind with Model.expression()).
5534
5527
  * @param [options.resetExpression=true] - Reset the expression to default after the motion is finished.
5535
- * @param [options.crossOrigin] - CORS settings for audio resources.
5536
5528
  * @param [options.onFinish] - Callback function when speaking completes.
5537
5529
  * @param [options.onError] - Callback function when an error occurs.
5538
5530
  * @return Promise that resolves with true if the motion is successfully started, with false otherwise.
@@ -5543,7 +5535,6 @@ class Live2DModel extends Container {
5543
5535
  volume = VOLUME,
5544
5536
  expression = void 0,
5545
5537
  resetExpression = true,
5546
- crossOrigin,
5547
5538
  onFinish,
5548
5539
  onError
5549
5540
  } = {}) {
@@ -5553,7 +5544,6 @@ class Live2DModel extends Container {
5553
5544
  volume,
5554
5545
  expression,
5555
5546
  resetExpression,
5556
- crossOrigin,
5557
5547
  onFinish,
5558
5548
  onError
5559
5549
  });
@@ -5563,7 +5553,6 @@ class Live2DModel extends Container {
5563
5553
  volume,
5564
5554
  expression,
5565
5555
  resetExpression,
5566
- crossOrigin,
5567
5556
  onFinish,
5568
5557
  onError
5569
5558
  });
@@ -5639,7 +5628,6 @@ class Live2DModel extends Container {
5639
5628
  * @param [options.volume] - Volume of the sound (0-1).
5640
5629
  * @param [options.expression] - In case you want to mix up an expression while playing sound (bind with Model.expression()).
5641
5630
  * @param [options.resetExpression=true] - Reset the expression to default after the motion is finished.
5642
- * @param {string} [options.crossOrigin] - CORS settings for audio resources.
5643
5631
  * @param [options.onFinish] - Callback function when speaking completes.
5644
5632
  * @param [options.onError] - Callback function when an error occurs.
5645
5633
  * @returns Promise that resolves with true if the sound is playing, false if it's not.
@@ -5648,7 +5636,6 @@ class Live2DModel extends Container {
5648
5636
  volume = VOLUME,
5649
5637
  expression,
5650
5638
  resetExpression = true,
5651
- crossOrigin,
5652
5639
  onFinish,
5653
5640
  onError
5654
5641
  } = {}) {
@@ -5656,7 +5643,6 @@ class Live2DModel extends Container {
5656
5643
  volume,
5657
5644
  expression,
5658
5645
  resetExpression,
5659
- crossOrigin,
5660
5646
  onFinish,
5661
5647
  onError
5662
5648
  });
@@ -6399,13 +6385,12 @@ class Cubism2InternalModel extends InternalModel {
6399
6385
  let value = this.motionManager.mouthSync();
6400
6386
  let min_ = 0;
6401
6387
  const max_ = 1;
6402
- const bias_weight = 1.2;
6403
- const bias_power = 0.7;
6388
+ const bias_power = 0.4;
6404
6389
  if (value > 0) {
6405
6390
  min_ = 0.4;
6406
6391
  }
6407
6392
  value = Math.pow(value, bias_power);
6408
- value = clamp(value * bias_weight, min_, max_);
6393
+ value = clamp(value, min_, max_);
6409
6394
  for (let i = 0; i < this.motionManager.lipSyncIds.length; ++i) {
6410
6395
  this.coreModel.setParamFloat(
6411
6396
  this.coreModel.getParamIndex(this.motionManager.lipSyncIds[i]),