diff --git a/src/nitro/Nitro.ts b/src/nitro/Nitro.ts index a2fd3f6a..3b0f6119 100644 --- a/src/nitro/Nitro.ts +++ b/src/nitro/Nitro.ts @@ -1,4 +1,5 @@ import { Application, SCALE_MODES, settings } from 'pixi.js'; +import { INitroManager } from '..'; import { ConfigurationEvent } from '../core/configuration/ConfigurationEvent'; import { EventDispatcher } from '../core/events/EventDispatcher'; import { IEventDispatcher } from '../core/events/IEventDispatcher'; @@ -28,6 +29,7 @@ import { IRoomSessionManager } from './session/IRoomSessionManager'; import { ISessionDataManager } from './session/ISessionDataManager'; import { RoomSessionManager } from './session/RoomSessionManager'; import { SessionDataManager } from './session/SessionDataManager'; +import { SoundManager } from './sound/SoundManager'; import { HabboWebTools } from './utils/HabboWebTools'; LegacyExternalInterface.available; @@ -57,6 +59,7 @@ export class Nitro extends Application implements INitro private _roomSessionManager: IRoomSessionManager; private _roomManager: IRoomManager; private _cameraManager: IRoomCameraWidgetManager; + private _soundManager: INitroManager; private _linkTrackers: ILinkEventTracker[]; private _workerTrackers: IWorkerEventTracker[]; @@ -98,6 +101,7 @@ export class Nitro extends Application implements INitro this._roomSessionManager = new RoomSessionManager(this._communication, this._roomEngine); this._roomManager = new RoomManager(this._roomEngine, this._roomEngine.visualizationFactory, this._roomEngine.logicFactory); this._cameraManager = new RoomCameraWidgetManager(); + this._soundManager = new SoundManager(); this._linkTrackers = []; this._workerTrackers = []; @@ -147,6 +151,8 @@ export class Nitro extends Application implements INitro if(this._avatar) this._avatar.init(); + if(this._soundManager) this._soundManager.init(); + if(this._roomEngine) { this._roomEngine.sessionDataManager = this._sessionDataManager; @@ -208,6 +214,13 @@ export class Nitro extends Application implements INitro this._avatar = null; } + if(this._soundManager) + { + this._soundManager.dispose(); + + this._soundManager = null; + } + if(this._communication) { this._communication.dispose(); diff --git a/src/nitro/events/NitroSoundEvent.ts b/src/nitro/events/NitroSoundEvent.ts new file mode 100644 index 00000000..02a56d9d --- /dev/null +++ b/src/nitro/events/NitroSoundEvent.ts @@ -0,0 +1,19 @@ +import { NitroEvent } from '../../core/events/NitroEvent'; + +export class NitroSoundEvent extends NitroEvent +{ + public static PLAY_SOUND: string = 'NSOE_PLAY_SOUND'; + + private _sampleCode: string; + + constructor(type: string, sampleCode: string) + { + super(type); + this._sampleCode = sampleCode; + } + + public get sampleCode(): string + { + return this._sampleCode; + } +} diff --git a/src/nitro/sound/SoundManager.ts b/src/nitro/sound/SoundManager.ts new file mode 100644 index 00000000..6e370067 --- /dev/null +++ b/src/nitro/sound/SoundManager.ts @@ -0,0 +1,208 @@ +import { AdvancedMap, Nitro, NitroSettingsEvent, RoomEngineEvent, RoomEngineObjectEvent, RoomEngineSamplePlaybackEvent } from '../..'; +import { NitroManager } from '../../core/common/NitroManager'; +import { NitroSoundEvent } from '../events/NitroSoundEvent'; +import { NitroEvent } from './../../core/events/NitroEvent'; + +export class SoundManager extends NitroManager +{ + private _volumeSystem: number; + private _volumeFurni: number; + private _volumeTrax: number; + + private _internalSamples: AdvancedMap; + private _furniSamples: AdvancedMap; + private _furnitureBeingPlayed: AdvancedMap; + + constructor() + { + super(); + + this._volumeSystem = 0.5; + this._volumeFurni = 0.5; + this._volumeTrax = 0.5; + + this._internalSamples = new AdvancedMap(); + this._furniSamples = new AdvancedMap(); + this._furnitureBeingPlayed = new AdvancedMap(); + + this.onEvent = this.onEvent.bind(this); + } + + private onEvent(event: NitroEvent) + { + switch(event.type) + { + case RoomEngineSamplePlaybackEvent.PLAY_SAMPLE: { + const castedEvent = (event as RoomEngineSamplePlaybackEvent); + + this.playFurniSample(castedEvent.objectId, castedEvent.sampleId, castedEvent.pitch); + return; + } + case RoomEngineObjectEvent.REMOVED: { + const castedEvent = (event as RoomEngineObjectEvent); + + this.stopFurniSample(castedEvent.objectId); + return; + } + case RoomEngineEvent.DISPOSED: { + this._furnitureBeingPlayed.getKeys().forEach((objectId: number) => + { + this.stopFurniSample(objectId); + }); + return; + } + case NitroSettingsEvent.SETTINGS_UPDATED: { + const castedEvent = (event as NitroSettingsEvent); + + const volumeFurniUpdated = castedEvent.volumeFurni !== this._volumeFurni; + + this._volumeSystem = castedEvent.volumeSystem; + this._volumeFurni = castedEvent.volumeFurni; + this._volumeTrax = castedEvent.volumeTrax; + + if(volumeFurniUpdated) this.updateFurniSamplesVolume(this._volumeFurni); + return; + } + case NitroSoundEvent.PLAY_SOUND: { + const castedEvent = (event as NitroSoundEvent); + + this.playInternalSample(castedEvent.sampleCode); + return; + } + } + } + + public onInit(): void + { + Nitro.instance.roomEngine.events.addEventListener(RoomEngineSamplePlaybackEvent.PLAY_SAMPLE, this.onEvent); + Nitro.instance.roomEngine.events.addEventListener(RoomEngineObjectEvent.REMOVED, this.onEvent); + Nitro.instance.roomEngine.events.addEventListener(RoomEngineEvent.DISPOSED, this.onEvent); + Nitro.instance.events.addEventListener(NitroSettingsEvent.SETTINGS_UPDATED, this.onEvent); + Nitro.instance.events.addEventListener(NitroSoundEvent.PLAY_SOUND, this.onEvent); + } + + public onDispose(): void + { + Nitro.instance.roomEngine.events.removeEventListener(RoomEngineSamplePlaybackEvent.PLAY_SAMPLE, this.onEvent); + Nitro.instance.roomEngine.events.removeEventListener(RoomEngineObjectEvent.REMOVED, this.onEvent); + Nitro.instance.roomEngine.events.removeEventListener(RoomEngineEvent.DISPOSED, this.onEvent); + Nitro.instance.events.removeEventListener(NitroSettingsEvent.SETTINGS_UPDATED, this.onEvent); + Nitro.instance.events.removeEventListener(NitroSoundEvent.PLAY_SOUND, this.onEvent); + } + + private playSample(sample: HTMLAudioElement, volume: number, pitch: number = 1): void + { + sample.volume = volume; + sample.currentTime = 0; + + try + { + sample.play(); + } + catch (e) + { + console.log(e); + } + } + + private playInternalSample(code: string): void + { + let sample = this._internalSamples.getValue(code); + + if(!sample) + { + const sampleUrl = Nitro.instance.getConfiguration('internal.samples.url'); + + sample = new Audio(sampleUrl.replace('%sample%', code)); + this._internalSamples.add(code, sample); + } + + this.playSample(sample, this._volumeSystem); + } + + private playFurniSample(objectId: number, code: number, pitch: number): void + { + let sample = this._furniSamples.getValue(code); + + if(!sample) + { + const sampleUrl = Nitro.instance.getConfiguration('external.samples.url'); + + sample = new Audio(sampleUrl.replace('%sample%', code.toString())); + this._furniSamples.add(code, sample); + } + + if(!this._furnitureBeingPlayed.hasKey(objectId)) this._furnitureBeingPlayed.add(objectId, code); + + sample.onended = (event) => + { + this.stopFurniSample(objectId); + }; + + sample.onpause = (event) => + { + this.stopFurniSample(objectId); + }; + + sample.onerror = (event) => + { + this.stopFurniSample(objectId); + }; + + this.playSample(sample, this._volumeFurni, pitch); + } + + private stopInternalSample(code: string): void + { + const sample = this._internalSamples.getValue(code); + + if(!sample) return; + + try + { + sample.pause(); + } + catch (e) + { + console.log(e); + } + } + + private stopFurniSample(objectId: number): void + { + const furnitureBeingPlayed = this._furnitureBeingPlayed.getValue(objectId); + + if(!furnitureBeingPlayed) return; + + const sample = this._furniSamples.getValue(furnitureBeingPlayed); + + this._furnitureBeingPlayed.remove(objectId); + + if(!sample) return; + + try + { + sample.pause(); + } + catch (e) + { + console.log(e); + } + } + + private updateInternalSamplesVolume(volume: number): void + { + this._internalSamples.getValues().forEach((sample: HTMLAudioElement) => + { + sample.volume = volume; + }); + } + + private updateFurniSamplesVolume(volume: number): void + { + this._furniSamples.getValues().forEach((sample: HTMLAudioElement) => + { + sample.volume = volume; + }); + } +}