Source: src/controllers/SoundManager.1.js

import { p5 } from "../../index.js";

/**
 * @class
 * @classdesc sound manager. singleton.
 * @memberof Managers
 */
export class SoundManager {
  static instance;

  /**
   * sound to potentially be played
   * @type {Sound}
   */
  sound = {};

  /**
   * currently selected sound
   * @type {Sound}
   */
  selectedSound = {};

  /**
   * list of sounds
   * @type {Sound[]}
   */
  soundList = [];

  /**
   * check if sound is loaded and ready to play
   */
  isReady = false;

  /**
   * Enforce singleton pattern
   * @returns {SoundManger}
   */
  constructor() {
    if (!SoundManager.instance) {
      SoundManager.instance = this;
    }

    return SoundManager.instance;
  }

  /**
   * add song to the sound list
   * @param {Sound} sound
   */
  add(sound) {
    this.soundList.push(sound);
  }

  /**
   * select a sound from the sound list
   * @param {string} name
   */
  select(name) {
    this.selectedSound = this.soundList.find((el) => el.name === name);
  }

  /**
   * select a next sound from the sound list
   */
  selectNext() {
    const currentIndex = this.soundList.findIndex(
      (el) => el.name === this.sound.name
    );
    const nextIndex = (currentIndex + 1) % this.soundList.length;
    this.selectedSound = this.soundList[nextIndex];
  }

  /**
   * select a previous sound from the sound list
   */
  selectPrevious() {
    const currentIndex = this.soundList.findIndex(
      (el) => el.name === this.sound.name
    );
    const previousIndex =
      (currentIndex - 1 + this.soundList.length) % this.soundList.length;
    this.selectedSound = this.soundList[previousIndex];
  }

  /**
   * load sound using p5 sound library, and set the sound property. return promise wrapping the loaded sound
   */
  loadSound() {
    this.sound.isReady = false;
    p5.loadSound(
      this.selectedSound.src,
      this.#onSuccess,
      this.#onError,
      this.#onLoading
    );
  }

  #onLoading = (prog) => {
    console.log("loading... " + prog);
    this.sound.isReady = false;
  };

  #onError = (err) => {
    console.error("error... " + err);
    this.sound.isReady = false;
  };

  #onSuccess = (loadedSound) => {
    if (this.sound?.loaded) {
      this.sound.loaded.stop();
    }

    this.sound = {
      ...this.selectedSound,
      loaded: loadedSound,
      duration: loadedSound.duration(),
    };

    this.sound.isReady = true;

    console.log(this.sound.isReady);
    if (this.sound.isReady && this.sound.loaded) {
      this.sound.loaded.setVolume(1);
      this.sound.loaded.loop();
    }
  };

  loop() {
    if (this.sound.isReady && this.sound.loaded) {
      this.sound.loaded.setVolume(1);
      this.sound.loaded.loop();
    }
  }
}