mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-24 13:55:04 +02:00
252 lines
5.3 KiB
TypeScript
252 lines
5.3 KiB
TypeScript
import { Injectable } from '@angular/core';
|
|
import { Howl } from 'howler';
|
|
import { random, sample } from 'lodash';
|
|
import { getUrl } from '../../client/rev';
|
|
import { Holiday, MapType, Season } from '../../common/commonInterfaces';
|
|
|
|
interface Track {
|
|
name: string;
|
|
src: string[];
|
|
howl?: Howl;
|
|
}
|
|
|
|
function getTracks(season: Season, holiday: Holiday, map: MapType) {
|
|
switch (map) {
|
|
case MapType.Island:
|
|
return [
|
|
'island',
|
|
'sunny-island',
|
|
];
|
|
case MapType.House:
|
|
return [
|
|
'happy-house',
|
|
'sweet-home',
|
|
];
|
|
case MapType.Cave:
|
|
return [
|
|
'cave-crystals',
|
|
'cave-secrets',
|
|
];
|
|
default:
|
|
return [
|
|
//'largo',
|
|
//'musicbox',
|
|
//'unrest',
|
|
'bossanova',
|
|
'clop',
|
|
'fivefour',
|
|
'hypnosis',
|
|
'scherzo',
|
|
'trills',
|
|
'waltzalt',
|
|
...(season === Season.Winter ? [
|
|
'trees-winter',
|
|
'reindeer-winter',
|
|
] : [
|
|
'trees',
|
|
'reindeer',
|
|
]),
|
|
'season',
|
|
'ambient',
|
|
'building',
|
|
'school',
|
|
'falling',
|
|
'tio',
|
|
'orchid',
|
|
...(season === Season.Winter ? [
|
|
'xmas-air',
|
|
'xmas-horns',
|
|
'xmas-presents',
|
|
] : []),
|
|
...(holiday === Holiday.Halloween ? [
|
|
'ghost',
|
|
'pumpkin',
|
|
] : []),
|
|
];
|
|
}
|
|
}
|
|
|
|
const FADE_TRACKS = true;
|
|
|
|
function fadeOut(track: Track, id: number, volume: number) {
|
|
const howl = track && track.howl;
|
|
|
|
if (howl) {
|
|
if (FADE_TRACKS) {
|
|
howl
|
|
.fade(volume, 0, 1000, id)
|
|
.once('fade', () => howl.pause(id).stop(id), id);
|
|
} else {
|
|
howl
|
|
.volume(0, id)
|
|
.pause(id)
|
|
.stop(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
function fadeIn(track: Track, id: number, volume: number) {
|
|
if (track && track.howl) {
|
|
track.howl.fade(0, volume, 1000, id);
|
|
}
|
|
}
|
|
|
|
interface Instance {
|
|
id: number;
|
|
track: Track;
|
|
}
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class Audio {
|
|
private tracks: Track[] = [];
|
|
private volume = 0;
|
|
private loops = 0;
|
|
private playing = false;
|
|
private stopped: Instance[] = [];
|
|
private instance?: Instance;
|
|
get trackName() {
|
|
return this.instance && this.volume ? this.instance.track.name : '';
|
|
}
|
|
initTracks(season: Season, holiday: Holiday, map: MapType) {
|
|
const tracks = getTracks(season, holiday, map);
|
|
|
|
// Make new tracks more frequent
|
|
// const duplicateTracks = tracks.filter(t => t === 'ghost' || t === 'pumpkin');
|
|
// tracks.push(...duplicateTracks);
|
|
// tracks.push(...duplicateTracks);
|
|
|
|
this.tracks = tracks.map(name => ({ name, src: [getUrl(`music/${name}.webm`), getUrl(`music/${name}.mp3`)] }));
|
|
this.loops = 0;
|
|
}
|
|
setVolume(volume: number) {
|
|
this.volume = volume / 100;
|
|
|
|
if (this.playing) {
|
|
if (this.instance) {
|
|
this.setInstanceVolume(this.instance, this.volume);
|
|
} else if (this.volume) {
|
|
this.playRandomTrack();
|
|
}
|
|
}
|
|
}
|
|
play() {
|
|
try {
|
|
if (!this.playing) {
|
|
this.playing = true;
|
|
|
|
if (this.volume) {
|
|
if (this.instance) {
|
|
this.resumeInstance(this.instance);
|
|
} else {
|
|
this.playRandomTrack();
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
playOrSwitchToRandomTrack() {
|
|
if (FADE_TRACKS) {
|
|
if (this.playing && this.volume) {
|
|
this.playRandomTrack();
|
|
} else {
|
|
this.play();
|
|
}
|
|
} else {
|
|
this.play();
|
|
}
|
|
}
|
|
stop() {
|
|
if (this.playing) {
|
|
this.playing = false;
|
|
this.stopInstance(this.instance);
|
|
}
|
|
}
|
|
forcePlay() {
|
|
if (!this.instance || !this.instance.track.howl!.playing(this.instance.id)) {
|
|
this.playRandomTrack();
|
|
}
|
|
}
|
|
touch() {
|
|
this.stopInstances();
|
|
this.setInstanceVolume(this.instance, this.volume);
|
|
}
|
|
private switchToTrack(track: Track) {
|
|
if (this.instance && this.instance.track === track) {
|
|
return false;
|
|
} else {
|
|
this.stopInstance(this.instance);
|
|
this.instance = this.playTrack(track);
|
|
return true;
|
|
}
|
|
}
|
|
playRandomTrack() {
|
|
while (!this.switchToTrack(sample(this.tracks)!))
|
|
;
|
|
|
|
this.loops = random(4, 7);
|
|
}
|
|
private playTrack(track: Track): Instance {
|
|
this.prepareTrack(track);
|
|
const id = track.howl!.play();
|
|
fadeIn(track, id, this.volume);
|
|
return { id, track };
|
|
}
|
|
private resumeInstance({ track, id }: Instance) {
|
|
track.howl!.play(id);
|
|
fadeIn(track, id, this.volume);
|
|
}
|
|
private stopInstance(instance: Instance | undefined) {
|
|
if (instance) {
|
|
this.stopped.push(instance);
|
|
}
|
|
|
|
this.stopInstances();
|
|
}
|
|
private stopInstances() {
|
|
this.stopped.forEach(({ track, id }) => fadeOut(track, id, this.volume));
|
|
this.stopped = this.stopped.filter(({ track, id }) => track.howl!.playing(id));
|
|
}
|
|
private setInstanceVolume(instance: Instance | undefined, volume: number) {
|
|
if (instance) {
|
|
const howl = instance.track.howl!;
|
|
howl.volume(volume, instance.id);
|
|
|
|
if (volume && !howl.playing(instance.id)) {
|
|
howl.play(instance.id);
|
|
} else if (!volume && howl.playing(instance.id)) {
|
|
howl.pause(instance.id);
|
|
}
|
|
}
|
|
}
|
|
private prepareTrack(track: Track) {
|
|
if (!track.howl) {
|
|
track.howl = new Howl({
|
|
src: track.src,
|
|
loop: true,
|
|
html5: true,
|
|
});
|
|
|
|
track.howl.on('end', id => this.onEnd(id));
|
|
}
|
|
}
|
|
private handlingOnEnd = 0;
|
|
private handlingOnEndAt = 0;
|
|
private onEnd(id: number) {
|
|
if (
|
|
this.instance && this.instance.id === id && --this.loops < 0 &&
|
|
(this.handlingOnEnd !== id || this.handlingOnEndAt < performance.now())
|
|
) {
|
|
this.handlingOnEnd = id;
|
|
this.handlingOnEndAt = performance.now() + 500;
|
|
|
|
if (this.volume && this.playing) {
|
|
this.playRandomTrack();
|
|
} else {
|
|
this.stopInstance(this.instance);
|
|
}
|
|
}
|
|
}
|
|
}
|