index.ts 3.42 KB
import { compact } from 'lodash';

const opt = Object.prototype.toString;

export const bytesForHuman = (bytes: number, decimals = 2) => {
  const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];

  let i = 0;

  // eslint-disable-next-line no-plusplus
  for (i; bytes > 1024; i++) {
    bytes /= 1024;
  }

  return `${parseFloat(bytes.toFixed(decimals))} ${units[i]}`;
};

export const audioBufferToWav = (audioBuffer: AudioBuffer, len: number) => {
  const numOfChan = audioBuffer.numberOfChannels;
  const length = len * numOfChan * 2 + 44;
  const buffer = new ArrayBuffer(length);
  const view = new DataView(buffer);
  const channels = [];
  let i;
  let sample;
  let offset = 0;
  let pos = 0;

  // write WAVE header
  // eslint-disable-next-line no-use-before-define
  setUint32(0x46464952); // "RIFF"
  // eslint-disable-next-line no-use-before-define
  setUint32(length - 8); // file length - 8
  // eslint-disable-next-line no-use-before-define
  setUint32(0x45564157); // "WAVE"

  // eslint-disable-next-line no-use-before-define
  setUint32(0x20746d66); // "fmt " chunk
  // eslint-disable-next-line no-use-before-define
  setUint32(16); // length = 16
  // eslint-disable-next-line no-use-before-define
  setUint16(1); // PCM (uncompressed)
  // eslint-disable-next-line no-use-before-define
  setUint16(numOfChan);
  // eslint-disable-next-line no-use-before-define
  setUint32(audioBuffer.sampleRate);
  // eslint-disable-next-line no-use-before-define
  setUint32(audioBuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
  // eslint-disable-next-line no-use-before-define
  setUint16(numOfChan * 2); // block-align
  // eslint-disable-next-line no-use-before-define
  setUint16(16); // 16-bit (hardcoded in this demo)

  // eslint-disable-next-line no-use-before-define
  setUint32(0x61746164); // "data" - chunk
  // eslint-disable-next-line no-use-before-define
  setUint32(length - pos - 4); // chunk length

  // write interleaved data
  // eslint-disable-next-line no-plusplus
  for (i = 0; i < audioBuffer.numberOfChannels; i++) {
    // @ts-ignore
    channels.push(audioBuffer.getChannelData(i));
  }

  while (pos < length) {
    // eslint-disable-next-line no-plusplus
    for (i = 0; i < numOfChan; i++) {
      // interleave channels
      sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
      // eslint-disable-next-line no-bitwise
      sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0; // scale to 16-bit signed int
      view.setInt16(pos, sample, true); // write 16-bit sample
      pos += 2;
    }
    // eslint-disable-next-line no-plusplus
    offset++; // next source sample
  }

  // create Blob
  return new Blob([buffer], { type: 'audio/wav' });

  function setUint16(data: number) {
    view.setUint16(pos, data, true);
    pos += 2;
  }

  function setUint32(data: number) {
    view.setUint32(pos, data, true);
    pos += 4;
  }
};

export const getLyricTimeArr = (lyric: string): string[] => {
  const times: string[] = [];

  lyric.split('\n').forEach((item) => {
    item = item.replace(/(^\s*)|(\s*$)/g, '');
    times.push(item.substring(item.indexOf('[') + 1, item.indexOf(']')));
  });

  return compact(times);
};

export function isUndefined(obj: any): obj is undefined {
  return obj === undefined;
}

export function isString(obj: any): obj is string {
  return opt.call(obj) === '[object String]';
}

export const promiseToBoolean = (callback: Promise<any>) => callback.then(() => true).catch(() => false);