lipSync.ts 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. import { LipSyncAnalyzeResult } from "./lipSyncAnalyzeResult";
  2. const TIME_DOMAIN_DATA_LENGTH = 2048;
  3. export class LipSync {
  4. public readonly audio: AudioContext;
  5. public readonly analyser: AnalyserNode;
  6. public readonly timeDomainData: Float32Array<ArrayBuffer>;
  7. public constructor(audio: AudioContext) {
  8. this.audio = audio;
  9. this.analyser = audio.createAnalyser();
  10. this.timeDomainData = new Float32Array(TIME_DOMAIN_DATA_LENGTH) as Float32Array<ArrayBuffer>;
  11. }
  12. public update(): LipSyncAnalyzeResult {
  13. this.analyser.getFloatTimeDomainData(this.timeDomainData);
  14. let volume = 0.0;
  15. for (let i = 0; i < TIME_DOMAIN_DATA_LENGTH; i++) {
  16. volume = Math.max(volume, Math.abs(this.timeDomainData[i]));
  17. }
  18. // cook
  19. volume = 1 / (1 + Math.exp(-45 * volume + 5));
  20. if (volume < 0.1) volume = 0;
  21. return {
  22. volume,
  23. };
  24. }
  25. public async playFromArrayBuffer(buffer: ArrayBuffer, onEnded?: () => void) {
  26. const audioBuffer = await this.audio.decodeAudioData(buffer);
  27. const bufferSource = this.audio.createBufferSource();
  28. bufferSource.buffer = audioBuffer;
  29. bufferSource.connect(this.audio.destination);
  30. bufferSource.connect(this.analyser);
  31. bufferSource.start();
  32. if (onEnded) {
  33. bufferSource.addEventListener("ended", onEnded);
  34. }
  35. }
  36. public async playFromURL(url: string, onEnded?: () => void) {
  37. const res = await fetch(url);
  38. const buffer = await res.arrayBuffer();
  39. this.playFromArrayBuffer(buffer, onEnded);
  40. }
  41. }