Files
everyone-can-use-english/enjoy/src/utils.ts
2024-08-29 14:02:52 +08:00

155 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Pitchfinder from "pitchfinder";
import { IPA_CONSONANTS, IPA_MAPPINGS, IPA_VOWELS } from "./constants";
export const extractFrequencies = (props: {
peaks: Float32Array;
sampleRate: number;
}): number[] => {
const { peaks, sampleRate } = props;
const detectPitch = Pitchfinder.AMDF({
sampleRate,
sensitivity: 0.05,
});
const duration = peaks.length / sampleRate;
const bpm = peaks.length / duration / 60;
const frequencies = Pitchfinder.frequencies(detectPitch, peaks, {
tempo: bpm,
quantization: bpm,
});
const cleanedFrequencies = removeNoise(frequencies);
return cleanedFrequencies;
};
export const removeNoise = (
numbers: number[],
threshold: number = 0.2
): number[] => {
numbers.forEach((num, i) => {
if (i === 0) return;
if (typeof num !== "number") return;
const prevNum = numbers[i - 1] || num;
const nextNum = numbers[i + 1] || num;
const avgNeighbor = (prevNum + nextNum) / 2.0;
const deviation = Math.abs(num - avgNeighbor);
if (deviation > threshold * avgNeighbor) {
numbers[i] = null;
}
});
return numbers;
};
export function milisecondsToTimestamp(ms: number) {
const hours = Math.floor(ms / 3600000).toString();
const minutes = Math.floor((ms % 3600000) / 60000).toString();
const seconds = Math.floor(((ms % 360000) % 60000) / 1000).toString();
const milliseconds = Math.floor(ms % 1000).toString();
return `${hours.padStart(2, "0")}:${minutes.padStart(
2,
"0"
)}:${seconds.padStart(2, "0")},${milliseconds}`;
}
export const convertWordIpaToNormal = (
ipas: string[],
options?: { mappings?: any }
): string[] => {
const { mappings = IPA_MAPPINGS } = options || {};
const consonants = Object.keys(IPA_CONSONANTS)
.map((key) => IPA_CONSONANTS[key])
.reduce((acc, val) => acc.concat(val), []);
const consonantsRegex = new RegExp(`^(\ˈ|ˌ)?` + consonants.join("|"));
const vowels = Object.keys(IPA_VOWELS)
.map((key) => IPA_VOWELS[key])
.reduce((acc, val) => acc.concat(val), []);
const vowelsRegex = new RegExp(`^(\ˈ|ˌ)?` + vowels.join("|"));
const converted: string[] = [];
// convert each ipa to normal
// if ipa is a vowel and marked, check if the previous ipa is a consonant,
// if so, mark the consonant instead
for (let i = 0; i < ipas.length; i++) {
const ipa = ipas[i];
converted.push(convertIpaToNormal(ipa, { mappings, marked: false }));
const isVowel = vowelsRegex.test(ipa);
const mark = ipa.match(/(\ˈ|ˌ)/);
let j = i - 1;
for (; j > 0 && j > i - 2; j--) {
if (
consonantsRegex.test(converted[j]) &&
!IPA_CONSONANTS.trill.includes(converted[j]) &&
!IPA_CONSONANTS.approximant.includes(converted[j]) &&
!IPA_CONSONANTS.lateralApproximant.includes(converted[j])
)
break;
if (
consonantsRegex.test(converted[j]) &&
!consonantsRegex.test(converted[j - 1])
) {
break;
}
}
if (isVowel && mark) {
if (converted[j] && consonantsRegex.test(converted[j])) {
converted[j] = mark[0] + converted[j];
} else {
converted[i] = mark[0] + converted[i];
}
}
}
return converted;
};
export const convertIpaToNormal = (
ipa: string,
options?: { mappings?: any; marked?: boolean }
): string => {
const { mappings = IPA_MAPPINGS, marked = false } = options || {};
const mark = ipa.match(/(\ˈ|ˌ)/);
const cleanIpa = ipa.replace(mark ? mark[0] : "", "");
const converted = mappings[cleanIpa] || cleanIpa;
if (mark && marked) {
return `${mark[0]}${converted}`;
} else {
return converted;
}
};
// make size of bytes human readable
export const humanFileSize = (bytes: number, si: boolean = false) => {
const thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return bytes + " B";
}
const units = si
? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
: ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
let u = -1;
const r = 10;
do {
bytes /= thresh;
++u;
} while (
Math.round(Math.abs(bytes) * r) / r >= thresh &&
u < units.length - 1
);
return bytes.toFixed(1) + " " + units[u];
};
export function getExtension(filename: string, defaultExt: string) {
return /(?:\.([^.]+))?$/.exec(filename)[1] || defaultExt;
}