mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
Remove SoundManager dependency (#1563)
All sound stuff is now handled directly by BattleSound, using the HTML5 audio API. The main complicated thing we do with sound is loop music with an intro. This is unfortunately not supported by ANY sound library out there (I had to manually add support for it myself to soundManager!) https://github.com/scottschiller/SoundManager2/pull/13 In the end, I don't think the existing libraries out there actually give us anything I care about.
This commit is contained in:
parent
54cbc51f39
commit
c1135497e0
|
|
@ -27,7 +27,7 @@ module.exports = {
|
|||
"BattleTextParser": false,
|
||||
|
||||
// Generic global variables
|
||||
"Config": false, "BattleSearch": false, "soundManager": false, "Storage": false, "Dex": false, "DexSearch": false,
|
||||
"Config": false, "BattleSearch": false, "Storage": false, "Dex": false, "DexSearch": false,
|
||||
"app": false, "toID": false, "toRoomid": false, "toUserid": false, "toName": false, "PSUtils": false, "MD5": false,
|
||||
"ChatHistory": false, "Topbar": false, "UserList": false,
|
||||
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -20,6 +20,7 @@ package-lock.json
|
|||
/js/battle-choices.js
|
||||
/js/battle-text-parser.js
|
||||
/js/battle-dex.js
|
||||
/js/battle-sound.js
|
||||
/js/battle-dex-data.js
|
||||
/js/battle-animations-moves.js
|
||||
/js/battle-animations.js
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ PS loads itself in phases:
|
|||
- `client-connection.ts`
|
||||
- Connect to server
|
||||
- Preact
|
||||
- SoundManager
|
||||
- BattleSound
|
||||
- `panel-mainmenu.tsx`
|
||||
- `panel-rooms.tsx`
|
||||
- `panels.tsx`
|
||||
|
|
|
|||
|
|
@ -102,10 +102,7 @@ ga('send', 'pageview');
|
|||
<script src="//play.pokemonshowdown.com/js/lib/jquery-2.1.4.min.js"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/jquery-cookie.js"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/autoresize.jquery.min.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/soundmanager2-nodebug-jsmin.js?"></script>
|
||||
<script>
|
||||
soundManager.setup({url: '//play.pokemonshowdown.com/swf/'});
|
||||
</script>
|
||||
<script src="//play.pokemonshowdown.com/js/battle-sound.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/html-css-sanitizer-minified.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/lodash.core.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/backbone.js?"></script>
|
||||
|
|
|
|||
|
|
@ -1279,9 +1279,7 @@
|
|||
if (autoscroll) {
|
||||
this.$chatFrame.scrollTop(this.$chat.height());
|
||||
}
|
||||
if (!app.focused && !Dex.prefs('mute') && Dex.prefs('notifvolume')) {
|
||||
soundManager.getSoundById('notif').setVolume(Dex.prefs('notifvolume')).play();
|
||||
}
|
||||
if (!app.focused) app.playNotificationSound();
|
||||
},
|
||||
addRow: function (line) {
|
||||
var name, name2, silent;
|
||||
|
|
@ -1386,9 +1384,7 @@
|
|||
|
||||
case 'notify':
|
||||
if (row[3] && !this.getHighlight(row[3])) return;
|
||||
if (!Dex.prefs('mute') && Dex.prefs('notifvolume')) {
|
||||
soundManager.getSoundById('notif').setVolume(Dex.prefs('notifvolume')).play();
|
||||
}
|
||||
app.playNotificationSound();
|
||||
this.notifyOnce(row[1], row[2], 'highlight');
|
||||
break;
|
||||
|
||||
|
|
@ -1396,9 +1392,7 @@
|
|||
var notifyOnce = row[4] !== '!';
|
||||
if (!notifyOnce) row[4] = '';
|
||||
if (row[4] && !this.getHighlight(row[4])) return;
|
||||
if (!this.notifications && !Dex.prefs('mute') && Dex.prefs('notifvolume')) {
|
||||
soundManager.getSoundById('notif').setVolume(Dex.prefs('notifvolume')).play();
|
||||
}
|
||||
if (!this.notifications) app.playNotificationSound();
|
||||
this.notify(row[2], row[3], row[1], notifyOnce);
|
||||
break;
|
||||
|
||||
|
|
@ -1655,9 +1649,7 @@
|
|||
}
|
||||
|
||||
if (mayNotify && isHighlighted) {
|
||||
if (!Dex.prefs('mute') && Dex.prefs('notifvolume')) {
|
||||
soundManager.getSoundById('notif').setVolume(Dex.prefs('notifvolume')).play();
|
||||
}
|
||||
app.playNotificationSound();
|
||||
var $lastMessage = this.$chat.children().last();
|
||||
var notifyTitle = "Mentioned by " + name + (this.id === 'lobby' ? '' : " in " + this.title);
|
||||
var notifyText = $lastMessage.html().indexOf('<span class="spoiler">') >= 0 ? '(spoiler)' : $lastMessage.children().last().text();
|
||||
|
|
|
|||
|
|
@ -63,17 +63,13 @@
|
|||
break;
|
||||
|
||||
case 'notify':
|
||||
if (!Dex.prefs('mute') && Dex.prefs('notifvolume')) {
|
||||
soundManager.getSoundById('notif').setVolume(Dex.prefs('notifvolume')).play();
|
||||
}
|
||||
app.playNotificationSound();
|
||||
this.notifyOnce(row[1], row.slice(2).join('|'), 'highlight');
|
||||
break;
|
||||
|
||||
case 'tempnotify':
|
||||
var notifyOnce = row[4] !== '!';
|
||||
if (!this.notifications && !Dex.prefs('mute') && Dex.prefs('notifvolume')) {
|
||||
soundManager.getSoundById('notif').setVolume(Dex.prefs('notifvolume')).play();
|
||||
}
|
||||
if (!this.notifications) app.playNotificationSound();
|
||||
this.notify(row[2], row[3], row[1], notifyOnce);
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -1885,6 +1885,12 @@ function toId() {
|
|||
Dex.prefs('autojoin', curAutojoin);
|
||||
},
|
||||
|
||||
playNotificationSound: function () {
|
||||
if (window.BattleSound && !Dex.prefs('mute')) {
|
||||
BattleSound.playSound('audio/notification.wav', Dex.prefs('notifvolume'));
|
||||
}
|
||||
},
|
||||
|
||||
/*********************************************************
|
||||
* Popups
|
||||
*********************************************************/
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ requireScript('https://play.pokemonshowdown.com/config/config.js?a7');
|
|||
requireScript('https://play.pokemonshowdown.com/js/lib/jquery-1.11.0.min.js');
|
||||
requireScript('https://play.pokemonshowdown.com/js/lib/lodash.compat.js');
|
||||
requireScript('https://play.pokemonshowdown.com/js/lib/html-sanitizer-minified.js');
|
||||
requireScript('https://play.pokemonshowdown.com/js/lib/soundmanager2-nodebug-jsmin.js');
|
||||
requireScript('https://play.pokemonshowdown.com/js/battle-sound.js');
|
||||
requireScript('https://play.pokemonshowdown.com/js/battledata.js?a7');
|
||||
requireScript('https://play.pokemonshowdown.com/data/pokedex-mini.js?a7');
|
||||
requireScript('https://play.pokemonshowdown.com/data/pokedex-mini-bw.js?a7');
|
||||
|
|
@ -67,17 +67,12 @@ var Replays = {
|
|||
// eslint-disable-next-line no-self-assign
|
||||
if (rc2) rc2.innerHTML = rc2.innerHTML;
|
||||
|
||||
if (window.soundManager && soundManager.ready) this.soundReady();
|
||||
if (window.HTMLAudioElement) $('.soundchooser, .startsoundchooser').show();
|
||||
this.reset();
|
||||
},
|
||||
"$": function (sel) {
|
||||
return this.$el.find(sel);
|
||||
},
|
||||
soundReady: function () {
|
||||
if (Replays.isSoundReady) return;
|
||||
Replays.isSoundReady = true;
|
||||
$('.soundchooser, .startsoundchooser').show();
|
||||
},
|
||||
clickChangeSetting: function (e) {
|
||||
e.preventDefault();
|
||||
var $chooser = $(e.currentTarget).closest('.chooser');
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@
|
|||
<script defer src="/js/battle-log.js?"></script>
|
||||
<script defer src="/js/panel-chat.js?"></script>
|
||||
|
||||
<script defer src="/js/lib/soundmanager2-nodebug-jsmin.js"></script>
|
||||
<script defer src="/js/battle-sound.js"></script>
|
||||
<script defer src="/js/lib/jquery-2.1.4.min.js"></script>
|
||||
<script defer src="/data/graphics.js?"></script>
|
||||
<script defer src="/data/text.js?"></script>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,6 @@
|
|||
} */
|
||||
//setTimeout(function(){updateProgress(true)}, 10000);
|
||||
|
||||
if (window.soundManager) {
|
||||
soundManager.onready(function(){
|
||||
soundManager.ready = true;
|
||||
$('.soundchooser, .startsoundchooser').show();
|
||||
});
|
||||
}
|
||||
|
||||
// Panels
|
||||
|
||||
var Topbar = Panels.Topbar.extend({
|
||||
|
|
@ -162,7 +155,7 @@ var ReplayPanel = Panels.StaticPanel.extend({
|
|||
var rc2 = this.$('.replay-controls-2')[0];
|
||||
if (rc2) rc2.innerHTML = rc2.innerHTML;
|
||||
|
||||
if (window.soundManager && soundManager.ready) this.$('.soundchooser, .startsoundchooser').show();
|
||||
if (window.HTMLAudioElement) this.$('.soundchooser, .startsoundchooser').show();
|
||||
},
|
||||
clickReplayDownloadButton: function (e) {
|
||||
var filename = (this.battle.tier || 'Battle').replace(/[^A-Za-z0-9]/g, '');
|
||||
|
|
|
|||
|
|
@ -88,10 +88,7 @@ function ThemeFooterTemplate() {
|
|||
|
||||
<script src="//play.pokemonshowdown.com/js/lib/jquery-cookie.js"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/html-sanitizer-minified.js"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/soundmanager2-nodebug-jsmin.js?"></script>
|
||||
<script>
|
||||
soundManager.setup({url: '//play.pokemonshowdown.com/swf/'});
|
||||
</script>
|
||||
<script src="//play.pokemonshowdown.com/js/battle-sound.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/config/config.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/battledata.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/pokedex-mini.js?"></script>
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ else if ($_REQUEST['name'])// && $REPLAYS[$_REQUEST['name']])
|
|||
<link rel="stylesheet" href="/style/replayer.css" />
|
||||
<script src="/js/jquery-1.9.1.min.js"></script>
|
||||
<script src="/js/jquery-cookie.js"></script>
|
||||
<script src="/js/soundmanager2.js"></script>
|
||||
<script src="/js/battle-sound.js"></script>
|
||||
<script src="/js/battledata.js"></script>
|
||||
<script src="/data/pokedex-mini.js"></script>
|
||||
<script src="/data/graphics.js"></script>
|
||||
|
|
|
|||
|
|
@ -30,17 +30,6 @@ This license DOES NOT extend to any other files in this repository.
|
|||
|
||||
*/
|
||||
|
||||
if (window.soundManager) {
|
||||
soundManager.setup({url: `https://${Config.routes.client}/swf/`});
|
||||
if (window.Replays) soundManager.onready(window.Replays.soundReady);
|
||||
soundManager.onready(() => {
|
||||
soundManager.createSound({
|
||||
id: 'notif',
|
||||
url: `https://${Config.routes.client}/audio/notification.wav`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class BattleScene {
|
||||
battle: Battle;
|
||||
animating = true;
|
||||
|
|
@ -1560,7 +1549,7 @@ class BattleScene {
|
|||
);
|
||||
if (nowPlaying) {
|
||||
if (!this.bgm) this.rollBgm();
|
||||
this.bgm!.play();
|
||||
this.bgm!.resume();
|
||||
} else if (this.bgm) {
|
||||
this.bgm.pause();
|
||||
}
|
||||
|
|
@ -2717,197 +2706,6 @@ Object.assign($.easing, {
|
|||
},
|
||||
});
|
||||
|
||||
interface SMSound {
|
||||
play(): this;
|
||||
pause(): this;
|
||||
stop(): this;
|
||||
resume(): this;
|
||||
setVolume(volume: number): this;
|
||||
setPosition(position: number): this;
|
||||
onposition(position: number, callback: (this: this) => void): this;
|
||||
position: number;
|
||||
readonly paused: boolean;
|
||||
playState: 0 | 1;
|
||||
isSoundPlaceholder?: boolean;
|
||||
}
|
||||
class BattleBGM {
|
||||
/**
|
||||
* May be shared with other BGM objects: every battle has its own BattleBGM
|
||||
* object, but two battles with the same music will have the same SMSound
|
||||
* object.
|
||||
*/
|
||||
sound: SMSound;
|
||||
isPlaying = false;
|
||||
constructor(sound: SMSound) {
|
||||
this.sound = sound;
|
||||
}
|
||||
play() {
|
||||
if (this.isPlaying) return;
|
||||
this.isPlaying = true;
|
||||
if (BattleSound.muted || !BattleSound.bgmVolume) return;
|
||||
let thisIsFirst = false;
|
||||
for (const bgm of BattleSound.bgm) {
|
||||
if (bgm === this) {
|
||||
thisIsFirst = true;
|
||||
} else if (bgm.isPlaying) {
|
||||
if (!thisIsFirst) return;
|
||||
bgm.sound.pause();
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.sound.setVolume(BattleSound.bgmVolume);
|
||||
// SoundManager bugs out if you call .play() while it's already playing
|
||||
if (!this.sound.playState || this.sound.paused) {
|
||||
this.sound.play();
|
||||
}
|
||||
}
|
||||
pause() {
|
||||
this.isPlaying = false;
|
||||
this.sound.pause();
|
||||
BattleBGM.update();
|
||||
}
|
||||
stop() {
|
||||
this.isPlaying = false;
|
||||
this.sound.stop();
|
||||
}
|
||||
destroy() {
|
||||
this.isPlaying = false;
|
||||
this.sound.stop();
|
||||
const soundIndex = BattleSound.bgm.indexOf(this);
|
||||
if (soundIndex >= 0) BattleSound.bgm.splice(soundIndex, 1);
|
||||
BattleBGM.update();
|
||||
}
|
||||
static update() {
|
||||
for (const bgm of BattleSound.bgm) {
|
||||
if (bgm.isPlaying) {
|
||||
if (BattleSound.muted || !BattleSound.bgmVolume) {
|
||||
bgm.sound.pause();
|
||||
} else {
|
||||
bgm.sound.setVolume(BattleSound.bgmVolume);
|
||||
// SoundManager bugs out if you call .play() while it's already playing
|
||||
if (!bgm.sound.playState || bgm.sound.paused) {
|
||||
bgm.sound.play();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const BattleSound = new class {
|
||||
effectCache: {[url: string]: SMSound} = {};
|
||||
|
||||
// bgm
|
||||
bgmCache: {[url: string]: SMSound} = {};
|
||||
bgm: BattleBGM[] = [];
|
||||
|
||||
// misc
|
||||
soundPlaceholder: SMSound = {
|
||||
play() { return this; },
|
||||
pause() { return this; },
|
||||
stop() { return this; },
|
||||
resume() { return this; },
|
||||
setVolume() { return this; },
|
||||
onposition() { return this; },
|
||||
isSoundPlaceholder: true,
|
||||
} as any;
|
||||
|
||||
// options
|
||||
effectVolume = 50;
|
||||
bgmVolume = 50;
|
||||
muted = false;
|
||||
|
||||
loadEffect(url: string) {
|
||||
if (this.effectCache[url] && !this.effectCache[url].isSoundPlaceholder) {
|
||||
return this.effectCache[url];
|
||||
}
|
||||
try {
|
||||
this.effectCache[url] = soundManager.createSound({
|
||||
id: url,
|
||||
url: Dex.resourcePrefix + url,
|
||||
volume: this.effectVolume,
|
||||
}) as SMSound;
|
||||
} catch {}
|
||||
if (!this.effectCache[url]) {
|
||||
this.effectCache[url] = this.soundPlaceholder;
|
||||
}
|
||||
return this.effectCache[url];
|
||||
}
|
||||
playEffect(url: string) {
|
||||
if (!this.muted) this.loadEffect(url).setVolume(this.effectVolume).play();
|
||||
}
|
||||
|
||||
addBgm(sound: SMSound, replaceBGM?: BattleBGM | null) {
|
||||
if (replaceBGM) {
|
||||
replaceBGM.sound.stop();
|
||||
replaceBGM.sound = sound;
|
||||
BattleBGM.update();
|
||||
return replaceBGM;
|
||||
}
|
||||
const bgm = new BattleBGM(sound);
|
||||
this.bgm.push(bgm);
|
||||
return bgm;
|
||||
}
|
||||
|
||||
/** loopstart and loopend are in milliseconds */
|
||||
loadBgm(url: string, loopstart: number, loopend: number, replaceBGM?: BattleBGM | null) {
|
||||
let sound = this.bgmCache[url];
|
||||
if (sound) {
|
||||
if (!sound.isSoundPlaceholder) {
|
||||
return this.addBgm(sound, replaceBGM);
|
||||
}
|
||||
}
|
||||
try {
|
||||
sound = soundManager.createSound({
|
||||
id: url,
|
||||
url: Dex.resourcePrefix + url,
|
||||
volume: this.bgmVolume,
|
||||
});
|
||||
} catch {}
|
||||
if (!sound) {
|
||||
// couldn't load
|
||||
// suppress crash
|
||||
return this.addBgm(this.bgmCache[url] = this.soundPlaceholder, replaceBGM);
|
||||
}
|
||||
sound.onposition(loopend, function () {
|
||||
this.setPosition(this.position - (loopend - loopstart));
|
||||
});
|
||||
this.bgmCache[url] = sound;
|
||||
return this.addBgm(sound, replaceBGM);
|
||||
}
|
||||
|
||||
// setting
|
||||
setMute(muted: boolean) {
|
||||
muted = !!muted;
|
||||
if (this.muted === muted) return;
|
||||
this.muted = muted;
|
||||
BattleBGM.update();
|
||||
}
|
||||
|
||||
loudnessPercentToAmplitudePercent(loudnessPercent: number) {
|
||||
// 10 dB is perceived as approximately twice as loud
|
||||
let decibels = 10 * Math.log(loudnessPercent / 100) / Math.log(2);
|
||||
return Math.pow(10, decibels / 20) * 100;
|
||||
}
|
||||
setBgmVolume(bgmVolume: number) {
|
||||
this.bgmVolume = this.loudnessPercentToAmplitudePercent(bgmVolume);
|
||||
BattleBGM.update();
|
||||
}
|
||||
setEffectVolume(effectVolume: number) {
|
||||
this.effectVolume = this.loudnessPercentToAmplitudePercent(effectVolume);
|
||||
}
|
||||
};
|
||||
if (typeof PS === 'object') {
|
||||
PS.prefs.subscribeAndRun(key => {
|
||||
if (!key || key === 'musicvolume' || key === 'effectvolume' || key === 'mute') {
|
||||
BattleSound.effectVolume = PS.prefs.effectvolume;
|
||||
BattleSound.bgmVolume = PS.prefs.musicvolume;
|
||||
BattleSound.muted = PS.prefs.mute;
|
||||
BattleBGM.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface AnimData {
|
||||
anim(scene: BattleScene, args: PokemonSprite[]): void;
|
||||
prepareAnim?(scene: BattleScene, args: PokemonSprite[]): void;
|
||||
|
|
|
|||
184
src/battle-sound.ts
Normal file
184
src/battle-sound.ts
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
|
||||
class BattleBGM {
|
||||
/**
|
||||
* May be shared with other BGM objects: every battle has its own BattleBGM
|
||||
* object, but two battles with the same music will have the same HTMLAudioElement
|
||||
* object.
|
||||
*/
|
||||
sound?: HTMLAudioElement;
|
||||
url: string;
|
||||
timer: number | undefined = undefined;
|
||||
loopstart: number;
|
||||
loopend: number;
|
||||
/**
|
||||
* When multiple battles with BGM are open, they will be `isPlaying`, but only the
|
||||
* first one will be `isActuallyPlaying`. In addition, muting volume or setting
|
||||
* BGM volume to 0 will set `isActuallyPlaying` to false.
|
||||
*/
|
||||
isPlaying = false;
|
||||
isActuallyPlaying = false;
|
||||
constructor(url: string, loopstart: number, loopend: number) {
|
||||
this.url = url;
|
||||
this.loopstart = loopstart;
|
||||
this.loopend = loopend;
|
||||
}
|
||||
play() {
|
||||
if (this.sound) this.sound.currentTime = 0;
|
||||
this.resume();
|
||||
}
|
||||
resume() {
|
||||
this.isPlaying = true;
|
||||
this.actuallyResume();
|
||||
}
|
||||
pause() {
|
||||
this.isPlaying = false;
|
||||
this.actuallyPause();
|
||||
BattleBGM.update();
|
||||
}
|
||||
stop() {
|
||||
this.pause();
|
||||
if (this.sound) this.sound.currentTime = 0;
|
||||
}
|
||||
destroy() {
|
||||
BattleSound.deleteBgm(this);
|
||||
this.pause();
|
||||
}
|
||||
|
||||
actuallyResume() {
|
||||
if (this !== BattleSound.currentBgm()) return;
|
||||
if (this.isActuallyPlaying) return;
|
||||
|
||||
if (!this.sound) this.sound = BattleSound.getSound(this.url);
|
||||
if (!this.sound) return;
|
||||
this.isActuallyPlaying = true;
|
||||
this.sound.volume = BattleSound.bgmVolume / 100;
|
||||
this.sound.play();
|
||||
this.updateTime();
|
||||
}
|
||||
actuallyPause() {
|
||||
if (!this.isActuallyPlaying) return;
|
||||
this.isActuallyPlaying = false;
|
||||
this.sound!.pause();
|
||||
this.updateTime();
|
||||
}
|
||||
/**
|
||||
* Handles the hard part of looping the sound
|
||||
*/
|
||||
updateTime() {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = undefined;
|
||||
if (this !== BattleSound.currentBgm()) return;
|
||||
if (!this.sound) return;
|
||||
|
||||
const progress = this.sound.currentTime * 1000;
|
||||
if (progress > this.loopend - 1000) {
|
||||
this.sound.currentTime -= (this.loopend - this.loopstart) / 1000;
|
||||
}
|
||||
|
||||
this.timer = setTimeout(() => {
|
||||
this.updateTime();
|
||||
}, Math.max(this.loopend - progress, 1));
|
||||
}
|
||||
|
||||
static update() {
|
||||
const current = BattleSound.currentBgm();
|
||||
for (const bgm of BattleSound.bgm) {
|
||||
if (bgm.isPlaying) {
|
||||
if (bgm === current) {
|
||||
bgm.actuallyResume();
|
||||
} else {
|
||||
bgm.actuallyPause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const BattleSound = new class {
|
||||
soundCache: {[url: string]: HTMLAudioElement | undefined} = {};
|
||||
|
||||
bgm: BattleBGM[] = [];
|
||||
|
||||
// options
|
||||
effectVolume = 50;
|
||||
bgmVolume = 50;
|
||||
muted = false;
|
||||
|
||||
getSound(url: string) {
|
||||
if (!window.HTMLAudioElement) return;
|
||||
if (this.soundCache[url]) return this.soundCache[url];
|
||||
try {
|
||||
const sound = document.createElement('audio');
|
||||
sound.src = 'https://' + Config.routes.client + '/' + url;
|
||||
sound.volume = this.effectVolume / 100;
|
||||
this.soundCache[url] = sound;
|
||||
return sound;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
playEffect(url: string) {
|
||||
this.playSound(url, this.muted ? 0 : this.effectVolume);
|
||||
}
|
||||
|
||||
playSound(url: string, volume: number) {
|
||||
if (!volume) return;
|
||||
const effect = this.getSound(url);
|
||||
if (effect) {
|
||||
effect.volume = volume / 100;
|
||||
effect.play();
|
||||
}
|
||||
}
|
||||
|
||||
/** loopstart and loopend are in milliseconds */
|
||||
loadBgm(url: string, loopstart: number, loopend: number, replaceBGM?: BattleBGM | null) {
|
||||
if (replaceBGM) this.deleteBgm(replaceBGM);
|
||||
|
||||
const bgm = new BattleBGM(url, loopstart, loopend);
|
||||
this.bgm.push(bgm);
|
||||
return bgm;
|
||||
}
|
||||
deleteBgm(bgm: BattleBGM) {
|
||||
const soundIndex = BattleSound.bgm.indexOf(bgm);
|
||||
if (soundIndex >= 0) BattleSound.bgm.splice(soundIndex, 1);
|
||||
}
|
||||
|
||||
currentBgm() {
|
||||
if (!this.bgmVolume || this.muted) return false;
|
||||
for (const bgm of this.bgm) {
|
||||
if (bgm.isPlaying) return bgm;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// setting
|
||||
setMute(muted: boolean) {
|
||||
muted = !!muted;
|
||||
if (this.muted === muted) return;
|
||||
this.muted = muted;
|
||||
BattleBGM.update();
|
||||
}
|
||||
|
||||
loudnessPercentToAmplitudePercent(loudnessPercent: number) {
|
||||
// 10 dB is perceived as approximately twice as loud
|
||||
let decibels = 10 * Math.log(loudnessPercent / 100) / Math.log(2);
|
||||
return Math.pow(10, decibels / 20) * 100;
|
||||
}
|
||||
setBgmVolume(bgmVolume: number) {
|
||||
this.bgmVolume = this.loudnessPercentToAmplitudePercent(bgmVolume);
|
||||
BattleBGM.update();
|
||||
}
|
||||
setEffectVolume(effectVolume: number) {
|
||||
this.effectVolume = this.loudnessPercentToAmplitudePercent(effectVolume);
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof PS === 'object') {
|
||||
PS.prefs.subscribeAndRun(key => {
|
||||
if (!key || key === 'musicvolume' || key === 'effectvolume' || key === 'mute') {
|
||||
BattleSound.effectVolume = PS.prefs.effectvolume;
|
||||
BattleSound.bgmVolume = PS.prefs.musicvolume;
|
||||
BattleSound.muted = PS.prefs.mute;
|
||||
BattleBGM.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
1
src/globals.d.ts
vendored
1
src/globals.d.ts
vendored
|
|
@ -3,7 +3,6 @@
|
|||
// dependencies
|
||||
///////////////
|
||||
|
||||
declare var soundManager: any;
|
||||
// Caja
|
||||
declare var html4: any;
|
||||
declare var html: any;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
<script src="js/battle-log.js"></script>
|
||||
<script src="js/panel-chat.js"></script>
|
||||
|
||||
<script src="js/lib/soundmanager2-nodebug-jsmin.js"></script>
|
||||
<script src="js/battle-sound.js"></script>
|
||||
<script src="js/lib/jquery-2.1.4.min.js"></script>
|
||||
<script src="data/graphics.js"></script>
|
||||
<script src="data/text.js"></script>
|
||||
|
|
|
|||
|
|
@ -71,11 +71,8 @@
|
|||
<script src="js/lib/jquery-2.1.4.min.js"></script>
|
||||
<script src="js/lib/jquery-cookie.js"></script>
|
||||
<script src="js/lib/autoresize.jquery.min.js"></script>
|
||||
<script src="js/lib/soundmanager2-nodebug-jsmin.js"></script>
|
||||
<script src="js/battle-sound.js"></script>
|
||||
<script src="config/testclient-key.js"></script>
|
||||
<script>
|
||||
soundManager.setup({url: 'swf/'});
|
||||
</script>
|
||||
<script src="js/lib/html-css-sanitizer-minified.js"></script>
|
||||
<script src="js/lib/lodash.core.js"></script>
|
||||
<script src="js/lib/backbone.js"></script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user