import { IAdvancedMap, IMessageEvent, IMusicController, IPlaylistController, ISongInfo } from '@nitrots/api'; import { GetCommunication, GetNowPlayingMessageComposer, GetSongInfoMessageComposer, GetUserSongDisksMessageComposer, TraxSongInfoMessageEvent, UserSongDisksInventoryMessageEvent } from '@nitrots/communication'; import { GetConfiguration } from '@nitrots/configuration'; import { GetEventDispatcher, NotifyPlayedSongEvent, NowPlayingEvent, RoomObjectSoundMachineEvent, SongDiskInventoryReceivedEvent, SongInfoReceivedEvent, SoundManagerEvent } from '@nitrots/events'; import { AdvancedMap } from '@nitrots/utils'; import { GetSoundManager } from '../GetSoundManager'; import { SongDataEntry, SongStartRequestData } from '../common'; import { TraxData } from '../trax/TraxData'; import { JukeboxPlaylistController } from './JukeboxPlaylistController'; import { MusicPlayer } from './MusicPlayer'; import { MusicPriorities } from './MusicPriorities'; export class MusicController implements IMusicController { public static readonly SKIP_POSITION_SET: number = -1; private static readonly MAXIMUM_NOTIFY_PRIORITY: number = MusicPriorities.PRIORITY_ROOM_PLAYLIST; private _timerInstance: number = 1; private _songRequestList: number[] = []; private _requestedSongs: Map = new Map(); private _availableSongs: Map = new Map(); private _songRequestsPerPriority: SongStartRequestData[] = []; private _songRequestCountsPerPriority: number[] = []; private _diskInventoryMissingData: number[] = []; private _songDiskInventory: IAdvancedMap = new AdvancedMap(); private _priorityPlaying: number = -1; private _requestNumberPlaying: number = -1; private _roomItemPlaylist: IPlaylistController; private _musicPlayer: MusicPlayer; private _songIdPlaying: number = 1; private _previousNotifiedSongId: number = -1; private _previousNotificationTime: number = -1; private _messageEvents: IMessageEvent[] = []; constructor() { this.onJukeboxInit = this.onJukeboxInit.bind(this); this.onJukeboxDispose = this.onJukeboxDispose.bind(this); this.onSoundMachineInit = this.onSoundMachineInit.bind(this); this.onSoundMachineDispose = this.onSoundMachineDispose.bind(this); this.onTraxSongComplete = this.onTraxSongComplete.bind(this); } public init(): void { // Store message events for cleanup this._messageEvents.push( GetCommunication().registerMessageEvent(new TraxSongInfoMessageEvent(this.onTraxSongInfoMessageEvent.bind(this))), GetCommunication().registerMessageEvent(new UserSongDisksInventoryMessageEvent(this.onSongDiskInventoryMessage.bind(this))) ); this._timerInstance = window.setInterval(this.onTick.bind(this), 1000); this._musicPlayer = new MusicPlayer(GetConfiguration().getValue('external.samples.url')); GetEventDispatcher().addEventListener(RoomObjectSoundMachineEvent.JUKEBOX_INIT, this.onJukeboxInit); GetEventDispatcher().addEventListener(RoomObjectSoundMachineEvent.JUKEBOX_DISPOSE, this.onJukeboxDispose); GetEventDispatcher().addEventListener(RoomObjectSoundMachineEvent.SOUND_MACHINE_INIT, this.onSoundMachineInit); GetEventDispatcher().addEventListener(RoomObjectSoundMachineEvent.SOUND_MACHINE_DISPOSE, this.onSoundMachineDispose); GetEventDispatcher().addEventListener(SoundManagerEvent.TRAX_SONG_COMPLETE, this.onTraxSongComplete); } public getRoomItemPlaylist(_arg_1?: number): IPlaylistController { return this._roomItemPlaylist; } public get songDiskInventory(): IAdvancedMap { return this._songDiskInventory; } public getSongDiskInventorySize(): number { return this._songDiskInventory.length; } public getSongDiskInventoryDiskId(k: number): number { if(((k >= 0) && (k < this._songDiskInventory.length))) { return this._songDiskInventory.getKey(k); } return -1; } public getSongDiskInventorySongId(k: number): number { if(((k >= 0) && (k < this._songDiskInventory.length))) { return this._songDiskInventory.getWithIndex(k); } return -1; } public getSongInfo(songId: number): ISongInfo { const _local_2: SongDataEntry = this.getSongDataEntry(songId); if(!_local_2) { this.requestSongInfoWithoutSamples(songId); } return _local_2; } public getSongIdPlayingAtPriority(priority: number): number { if(priority !== this._priorityPlaying) { return -1; } return this._songIdPlaying; } public stop(priority: number): void { const isCurrentPlayingPriority = (priority === this._priorityPlaying); const isTopRequestPriority = (this.getTopRequestPriority() === priority); if(isCurrentPlayingPriority) { this.resetSongStartRequest(priority); this.stopSongAtPriority(priority); } else { this.resetSongStartRequest(priority); if(isTopRequestPriority) { this.reRequestSongAtPriority(this._priorityPlaying); } } } public addSongInfoRequest(k: number): void { this.requestSong(k, true); } public requestSongInfoWithoutSamples(k: number): void { this.requestSong(k, false); } public requestUserSongDisks(): void { GetCommunication().connection.send(new GetUserSongDisksMessageComposer()); } public updateVolume(_arg_1: number): void { this._musicPlayer.setVolume(_arg_1); } public dispose(): void { if(this._timerInstance) { clearInterval(this._timerInstance); this._timerInstance = undefined; } // Remove message events for(const event of this._messageEvents) { GetCommunication().removeMessageEvent(event); } this._messageEvents = []; GetEventDispatcher().removeEventListener(RoomObjectSoundMachineEvent.JUKEBOX_INIT, this.onJukeboxInit); GetEventDispatcher().removeEventListener(RoomObjectSoundMachineEvent.JUKEBOX_DISPOSE, this.onJukeboxDispose); GetEventDispatcher().removeEventListener(RoomObjectSoundMachineEvent.SOUND_MACHINE_INIT, this.onSoundMachineInit); GetEventDispatcher().removeEventListener(RoomObjectSoundMachineEvent.SOUND_MACHINE_DISPOSE, this.onSoundMachineDispose); GetEventDispatcher().removeEventListener(SoundManagerEvent.TRAX_SONG_COMPLETE, this.onTraxSongComplete); } public get samplesIdsInUse(): number[] { let _local_3: SongStartRequestData; let _local_4: SongDataEntry; let k = []; for(let i = 0; i < this._songRequestsPerPriority.length; i++) { if(this._songRequestsPerPriority[i]) { _local_3 = this._songRequestsPerPriority[i]; _local_4 = this._availableSongs.get(_local_3.songId); if(_local_4) { const songData = _local_4.songData; if(songData.length > 0) { const traxData = new TraxData(songData); k = k.concat(traxData.getSampleIds()); } } } } return k; } public onSongLoaded(songId: number): void { const priority = this.getTopRequestPriority(); if(priority >= 0) { const songIdAtTopPriority = this.getSongIdRequestedAtPriority(priority); if(songId === songIdAtTopPriority) { this.playSongObject(priority, songId); } } } public samplesUnloaded(_arg_1: number[]): void { throw new Error('Method not implemented.'); } protected onTraxSongComplete(k: SoundManagerEvent): void { if(this.getSongIdPlayingAtPriority(this._priorityPlaying) === k.id) { if(((this.getTopRequestPriority() === this._priorityPlaying) && (this.getSongRequestCountAtPriority(this._priorityPlaying) == this._requestNumberPlaying))) { this.resetSongStartRequest(this._priorityPlaying); } const priorityPlaying = this._priorityPlaying; this.playSongWithHighestPriority(); if(priorityPlaying >= MusicPriorities.PRIORITY_SONG_PLAY) { GetEventDispatcher().dispatchEvent(new NowPlayingEvent(NowPlayingEvent.NPW_USER_STOP_SONG, priorityPlaying, k.id, -1)); } } } private onTraxSongInfoMessageEvent(event: TraxSongInfoMessageEvent): void { const parser = event.getParser(); for(const song of parser.songs) { const songAvailable = !!this.getSongDataEntry(song.id); const areSamplesRequested = !!this.areSamplesRequested(song.id); if(!songAvailable) { if(!areSamplesRequested) { //_local_9 = this._soundManager.loadTraxSong(_local_6.id, _local_6.data); } const songInfoEntry: SongDataEntry = new SongDataEntry(song.id, song.length, song.name, song.creator, song.data); this._availableSongs.set(song.id, songInfoEntry); const topRequestPriotityIndex: number = this.getTopRequestPriority(); const songId: number = this.getSongIdRequestedAtPriority(topRequestPriotityIndex); if(song.id === songId) { this.playSongObject(topRequestPriotityIndex, songId); } GetEventDispatcher().dispatchEvent(new SongInfoReceivedEvent(SongInfoReceivedEvent.SIR_TRAX_SONG_INFO_RECEIVED, song.id)); while(this._diskInventoryMissingData.indexOf(song.id) != -1) { this._diskInventoryMissingData.splice(this._diskInventoryMissingData.indexOf(song.id), 1); if(this._diskInventoryMissingData.length === 0) { GetEventDispatcher().dispatchEvent(new SongDiskInventoryReceivedEvent(SongDiskInventoryReceivedEvent.SDIR_SONG_DISK_INVENTORY_RECEIVENT_EVENT)); } } } } } private onSongDiskInventoryMessage(event: UserSongDisksInventoryMessageEvent): void { const parser = event.getParser(); this._songDiskInventory.reset(); for(let i = 0; i < parser.songDiskCount; i++) { const diskId = parser.getDiskId(i); const songId = parser.getSongId(i); this._songDiskInventory.add(diskId, songId); if(!this._availableSongs.get(songId)) { this._diskInventoryMissingData.push(songId); this.requestSongInfoWithoutSamples(songId); } } if(this._diskInventoryMissingData.length === 0) { GetEventDispatcher().dispatchEvent(new SongDiskInventoryReceivedEvent(SongDiskInventoryReceivedEvent.SDIR_SONG_DISK_INVENTORY_RECEIVENT_EVENT)); } } private onTick(): void { if(this._songRequestList.length === 0) return; GetCommunication().connection.send(new GetSongInfoMessageComposer(...this._songRequestList)); this._songRequestList = []; } private requestSong(songId: number, arg2: boolean): void { if(this._requestedSongs.get(songId) === undefined) { this._requestedSongs.set(songId, arg2); this._songRequestList.push(songId); } } private areSamplesRequested(k: number): boolean { if(!this._requestedSongs.get(k)) { return false; } return this._requestedSongs.get(k); } private processSongEntryForPlaying(k: number, _arg_2: boolean = true): boolean { const songData: SongDataEntry = this.getSongDataEntry(k); if(!songData) { this.addSongInfoRequest(k); return false; } /* if(_local_3.soundObject == null) { _local_3.soundObject = this._soundManager.loadTraxSong(_local_3.id, _local_3.songData); } const _local_4:IHabboSound = _local_3.soundObject; if(!_local_4.ready) { return false; } */ return true; } public playSong(songId: number, priority: number, startPos: number = 0, playLength: number = 0, fadeInSeconds: number = 0.5, fadeOutSeconds: number = 0.5): boolean { if(!this.addSongStartRequest(priority, songId, startPos, playLength, fadeInSeconds, fadeOutSeconds)) { return false; } if(!this.processSongEntryForPlaying(songId)) { return false; } if(priority >= this._priorityPlaying) { this.playSongObject(priority, songId); } return true; } private playSongObject(priority: number, songId: number): boolean { if((((songId === -1) || (priority < 0)) || (priority >= MusicPriorities.PRIORITY_COUNT))) { return false; } let _local_3 = false; if(this.stopSongAtPriority(this._priorityPlaying)) { _local_3 = true; } const songData: SongDataEntry = this.getSongDataEntry(songId); if(!songData) { return false; } if(_local_3) { return true; } this._musicPlayer.setVolume(GetSoundManager().traxVolume); let startPos = MusicController.SKIP_POSITION_SET; let playLength = 0; let fadeInSeconds = 2; let fadeOutSeconds = 1; const songRequestData: SongStartRequestData = this.getSongStartRequest(priority); if(songRequestData) { startPos = songRequestData.startPos; playLength = songRequestData.playLength; fadeInSeconds = songRequestData.fadeInSeconds; fadeOutSeconds = songRequestData.fadeOutSeconds; } if(startPos >= (songData.length / 1000)) { return false; } if(startPos <= MusicController.SKIP_POSITION_SET) { startPos = 0; } startPos = Math.trunc(startPos); /* _local_5.fadeInSeconds = _local_8; _local_5.fadeOutSeconds = _local_9; _local_5.position = _local_6; _local_5.play(_local_7); */ this._priorityPlaying = priority; this._requestNumberPlaying = this.getSongRequestCountAtPriority(priority); this._songIdPlaying = songId; if(this._priorityPlaying <= MusicController.MAXIMUM_NOTIFY_PRIORITY) { this.notifySongPlaying(songData); } this._musicPlayer.preloadSamplesForSong(songData.songData).then(() => this._musicPlayer.play(songData.songData, songData.id, startPos, playLength)); if(priority > MusicPriorities.PRIORITY_ROOM_PLAYLIST) { GetEventDispatcher().dispatchEvent(new NowPlayingEvent(NowPlayingEvent.NPE_USER_PLAY_SONG, priority, songData.id, -1)); } return true; } private notifySongPlaying(k: SongDataEntry): void { const _local_2 = 8000; const timeNow = Date.now(); if(((k.length >= _local_2) && ((!(this._previousNotifiedSongId == k.id)) || (timeNow > (this._previousNotificationTime + _local_2))))) { GetEventDispatcher().dispatchEvent(new NotifyPlayedSongEvent(k.name, k.creator)); this._previousNotifiedSongId = k.id; this._previousNotificationTime = timeNow; } } private addSongStartRequest(priority: number, songId: number, startPos: number, playLength: number, fadeInSeconds: number, fadeOutSeconds: number): boolean { if(((priority < 0) || (priority >= MusicPriorities.PRIORITY_COUNT))) { return false; } const songStartRequest = new SongStartRequestData(songId, startPos, playLength, fadeInSeconds, fadeOutSeconds); this._songRequestsPerPriority[priority] = songStartRequest; this._songRequestCountsPerPriority[priority] = (this._songRequestCountsPerPriority[priority] + 1); return true; } private getSongDataEntry(k: number): SongDataEntry { let entry: SongDataEntry; if(this._availableSongs) { entry = (this._availableSongs.get(k)); } return entry; } private getSongStartRequest(k: number): SongStartRequestData { return this._songRequestsPerPriority[k]; } private getTopRequestPriority(): number { return this._songRequestsPerPriority.length - 1; } private getSongIdRequestedAtPriority(priorityIndex: number): number { if(priorityIndex < 0 || priorityIndex >= MusicPriorities.PRIORITY_COUNT) return -1; if(!this._songRequestsPerPriority[priorityIndex]) return -1; return this._songRequestsPerPriority[priorityIndex].songId; } private getSongRequestCountAtPriority(k: number): number { if(((k < 0) || (k >= MusicPriorities.PRIORITY_COUNT))) { return -1; } return this._songRequestCountsPerPriority[k]; } private playSongWithHighestPriority(): void { let _local_3: number; this._priorityPlaying = -1; this._songIdPlaying = -1; this._requestNumberPlaying = -1; const k = this.getTopRequestPriority(); let _local_2 = k; while(_local_2 >= 0) { _local_3 = this.getSongIdRequestedAtPriority(_local_2); if(((_local_3 >= 0) && (this.playSongObject(_local_2, _local_3)))) { return; } _local_2--; } } private resetSongStartRequest(priority: number): void { if(((priority >= 0) && (priority < MusicPriorities.PRIORITY_COUNT))) { this._songRequestsPerPriority[priority] = undefined; } } private reRequestSongAtPriority(k: number): void { this._songRequestCountsPerPriority[k] = (this._songRequestCountsPerPriority[k] + 1); } private stopSongAtPriority(priority: number): boolean { if(((priority === this._priorityPlaying) && (this._priorityPlaying >= 0))) { const songIdAtPriority = this.getSongIdPlayingAtPriority(priority); if(songIdAtPriority >= 0) { const songData = this.getSongDataEntry(songIdAtPriority); //this.stopSongDataEntry(_local_3); this._musicPlayer.stop(); return true; } } return false; } private onSoundMachineInit(k: Event): void { this.disposeRoomPlaylist(); //this._roomItemPlaylist = (new SoundMachinePlayListController(this._soundManager, this, this._events) as IPlaylistController); } private onSoundMachineDispose(k: Event): void { this.disposeRoomPlaylist(); } private onJukeboxInit(k: Event): void { this.disposeRoomPlaylist(); this._roomItemPlaylist = (new JukeboxPlaylistController() as IPlaylistController); this._roomItemPlaylist.init(); GetCommunication().connection.send(new GetNowPlayingMessageComposer()); } private onJukeboxDispose(k: Event): void { this.disposeRoomPlaylist(); } private disposeRoomPlaylist(): void { if(this._roomItemPlaylist) { this._roomItemPlaylist.dispose(); this._roomItemPlaylist = undefined; } } }