//============================================================================= // QAudio //============================================================================= var Imported = Imported || {}; if (!Imported.QPlus || !QPlus.versionCheck(Imported.QPlus, '1.4.0')) { alert('Error: QAudio requires QPlus 1.4.0 or newer to work.'); throw new Error('Error: QAudio requires QPlus 1.4.0 or newer to work.'); } Imported.QAudio = '2.3.4'; //============================================================================= /*: * @plugindesc * Few new audio features * @version 2.3.4 * @author Quxios | Version 2.3.4 * @site https://quxios.github.io/ * @updateurl https://quxios.github.io/data/pluginsMin.json * * @requires QPlus * * @param Default Radius * @desc Default radius in tiles * @type Number * @min 0 * @default 5 * * @param Default Max Volume * @desc Default max volume * @type Number * @min 0 * @default 100 * * @help * ============================================================================ * ## About * ============================================================================ * This plugin allows you to play an audio (bgm, bgs, me or se) at a fixed * position. The volume and panning will be dynamically updated based off of * the players distance from that audios location. * ============================================================================ * ## Plugin Commands * ============================================================================ * **Playing / Looping** * ---------------------------------------------------------------------------- * ~~~ * qAudio start [AUDIONAME] [list of options] * ~~~ * AUDIONAME - The name of the audio you want to play. If file has spaces in it * wrap the name with "". Ex. If audio name was: Some File, then use "Some File" * * Possible options: * * - idX: where X the ID for the audio (needed for stopping) * - type: bgm, bgs, me, or se (Default: bgm) * - loop: this audio will loop * - noPan: this audio will not update its pan * - pitchX: where X is the pitch %, default is 100 * - maxV: where V is the max volume for this audio, between 0-100 * - fadeinT: where T is the time to fade in, in seconds * - xX: where X is the x position for the audio * - yY: where Y is the y position for the audio * - radiusR: where R is the max radius for the audio * - bindToCHARAID: where CHARAID is the character to bind to. * * CHARAID - The character identifier. * - For player: 0, p, or player * - For events: EVENTID, eEVENTID, eventEVENTID or this for the event that * called this (replace EVENTID with a number) * * *Note: If no x, y or bindTo are set, it will play at players position* * ---------------------------------------------------------------------------- * **Examples 1** * ---------------------------------------------------------------------------- * ~~~ * qAudio start Battle1 * ~~~ * Will play bgm Battle1 at the players location. Recommended not to do it this * way, best to set options. * * ~~~ * qAudio start City bgs idCITY max80 radius10 x5 y5 * ~~~ * Will play bgs City at position (5, 5) with a radius of 10, max volume of 80 * and it's id is CITY * * ~~~ * qAudio start City bgs idCITY radius5 bindTo1 loop * qAudio start City bgs idCITY radius5 bindToE1 loop * qAudio start City bgs idCITY radius5 bindToEvent1 loop * ~~~ * *Note: All 3 are the same, just using a different character id method* * * Will play bgs City at event 1s position and move with that event. It also has * a radius of 5 and it will keep looping. It's id is CITY * ---------------------------------------------------------------------------- * **Stoping a QAudio** * ---------------------------------------------------------------------------- * ~~~ * qAudio stop ID [list of options] * ~~~ * - ID: The ID you set for the audio, set this to ALL to stop all QAudios * * Possible options: * * - fadeoutT: where T is the time to fade out, in seconds * * To clear/stop all qAudios * ~~~ * qAudio clear * ~~~ * ---------------------------------------------------------------------------- * **Examples 2** * ---------------------------------------------------------------------------- * First make a qAudio with an Id like: * ~~~ * qAudio start Battle1 bgm idAb1 * ~~~ * * Then when you want to clear it: * ~~~ * qAudio stop Ab1 * ~~~ * * Or clear it with a fadeout * ~~~ * qAudio stop Ab1 fadeOut2 * ~~~ * ---------------------------------------------------------------------------- * **Saving/Restoring QAudios** * ---------------------------------------------------------------------------- * To save all the current QAudios that are playing use: * ~~~ * qAudio save * ~~~ * * To play all the QAudios that were saved use: * ~~~ * qAudio restore * ~~~ * *Note: Saving does not remember the position the audio left off at* * ============================================================================ * ## Links * ============================================================================ * Formated Help: * * https://quxios.github.io/#/plugins/QAudio * * RPGMakerWebs: * * http://forums.rpgmakerweb.com/index.php?threads/qplugins.73023/ * * Terms of use: * * https://github.com/quxios/QMV-Master-Demo/blob/master/readme.md * * Like my plugins? Support me on Patreon! * * https://www.patreon.com/quxios * * @tags audio, character, proximity */ //============================================================================= //============================================================================= // QAudio (function() { var _PARAMS = QPlus.getParams('', true); var _DEFAULTRADIUS = _PARAMS['Default Radius']; var _DEFAULTVOLUME = _PARAMS['Default Max Volume']; //----------------------------------------------------------------------------- // Game_Interpreter // // The interpreter for running event commands. var Alias_Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand; Game_Interpreter.prototype.pluginCommand = function(command, args) { if (command.toLowerCase() === 'qaudio') { return this.qAudioCommand(QPlus.makeArgs(args)); } Alias_Game_Interpreter_pluginCommand.call(this, command, args); }; Game_Interpreter.prototype.qAudioCommand = function(args) { var cmd = args[0].toLowerCase(); if (cmd === 'loop' || cmd === 'play') { return this.qAudioCommandOld(cmd, args) } if (cmd === 'start') { var name = args[1]; var args2 = args.slice(2); var loop = !!QPlus.getArg(args2, /^loop$/i); var dontPan = !!QPlus.getArg(args2, /^noPan$/i); var id = QPlus.getArg(args2, /^id(.+)/i) || '*'; id = !id || id === '*' ? this.getUniqueQAudioId() : id; var type = QPlus.getArg(args2, /^(bgm|bgs|me|se)$/i) || 'bgm'; type = type.toLowerCase(); var max = QPlus.getArg(args2, /^max(\d+)/i); if (max === null) { max = _DEFAULTVOLUME; } max = Number(max) / 100; var radius = QPlus.getArg(args2, /^radius(\d+)/i); radius = radius === null ? _DEFAULTRADIUS : Number(radius); var bindTo = QPlus.getArg(args2, /^bindTo(.+)/i); if (bindTo) { if (bindTo.toLowerCase() === 'this') { bindTo = this.character(0).charaId(); } } var x = QPlus.getArg(args2, /^x(\d+)/i); x = x === null ? $gamePlayer.x : Number(x); var y = QPlus.getArg(args2, /^y(\d+)/i); y = y === null ? $gamePlayer.y : Number(y); var fadein = QPlus.getArg(args2, /^fadein(\d+)/i); fadein = fadein === null ? 0 : Number(fadein); var pitch = QPlus.getArg(args2, /^pitch(\d+)/i); pitch = pitch === null ? 100 : Number(pitch); var audio = { name: name, volume: 100, pitch: pitch / 100, pan: 0 } AudioManager.playQAudio(id, audio, { type: type, loop: loop, maxVolume: max, radius: radius, x: x, y: y, bindTo: bindTo, doPan: !dontPan, fadeIn: fadein }) return; } if (cmd === 'stop') { var id = args[1]; var fadeOut = Number(QPlus.getArg(args, /^fadeout(\d+)/i)); if (id === 'all') { if (fadeOut) { AudioManager.fadeOutAllQAudio(fadeOut); } else { AudioManager.stopAllQAudio(); } } else { if (fadeOut) { AudioManager.fadeOutQAudio(id, fadeOut); } else { AudioManager.stopQAudio(id); } } return; } if (cmd === 'clear') { return AudioManager.stopAllQAudio(); } if (cmd === 'save') { return AudioManager.saveQAudioa(); } if (cmd === 'restore') { return AudioManager.restoreQAudios(); } }; Game_Interpreter.prototype.qAudioCommandOld = function(cmd, args) { var loop = cmd === 'loop'; var id = args[1]; id = id === '*' ? this.getUniqueQAudioId() : id; var type = args[2].toLowerCase(); var audio = { name: args[3], volume: 100, pitch: 0, pan: 0 } var max = Number(args[4]) / 100; var r = Number(args[5]); var x = Number(args[6]); var y = Number(args[7]); var bindTo = null; if (!y && y !== 0) { if (x === 0) { bindTo = 0; } else { bindTo = $gameMap.event(x).charaId(); } x = null; y = null; } AudioManager.playQAudio(id, audio, { type: type, loop: loop, maxVolume: max, radius: radius, x: x, y: y, bindTo: bindTo }); }; Game_Interpreter.prototype.getUniqueQAudioId = function() { var id = '*0'; var counter = 0; var newId = false; while (!newId) { if (AudioManager._QAudioBuffers.length === 0) { newId = true; break; } counter++; id = '*' + counter; var j = 0; for (var i = 0; i < AudioManager._QAudioBuffers.length; i++) { if (AudioManager._QAudioBuffers[i].uid !== id) { j++; } } newId = j === i; } return id; }; //----------------------------------------------------------------------------- // AudioManager AudioManager._QAudioBuffers = []; AudioManager._savedQAudios = []; AudioManager.playQAudio = function(id, audio, options) { if (audio.name) { this._QAudioBuffers = this._QAudioBuffers.filter(function(b) { if (b.uid === id) { b.stop(); b = null; return false; } return true; }) var buffer = this.createBuffer(options.type, audio.name); if (options.bindTo !== undefined && options.bindTo !== null) { buffer.bindTo = options.bindTo; } else { buffer.mapX = options.x; buffer.mapY = options.y; } buffer.uid = id; buffer.type = options.type; buffer.radius = options.radius; buffer.maxVolume = options.maxVolume; buffer.doPan = options.doPan; buffer.rVolume = 1; buffer.fadingIn = null; buffer.pitch = audio.pitch; if (options.fadeIn) { buffer.rVolume = 0; buffer.fadingIn = { t: 0, dur: options.fadeIn * 60 } } this.updateQAudioDistance(buffer); buffer.play(options.loop, 0); if (!options.loop) { buffer.addStopListener(this.stopQAudio.bind(this, id)); } this._QAudioBuffers.push(buffer); $gameSystem._QAudios.push({ id: id, audio: audio, options: options }); } }; AudioManager.stopQAudio = function(id) { var buffers = this._QAudioBuffers; for (var i = buffers.length - 1; i >= 0; i--) { if (buffers[i] && buffers[i].uid === id) { buffers[i].stop(); buffers[i] = null; buffers.splice(i, 1); $gameSystem._QAudios.splice(i, 1); break; } } }; AudioManager.stopAllQAudio = function() { var buffers = this._QAudioBuffers; for (var i = 0; i < buffers.length; i++) { if (buffers[i]._stopListeners) { buffers[i]._stopListeners.length = 0; } buffers[i].stop(); buffers[i] = null; } this._QAudioBuffers = []; if ($gameSystem) { $gameSystem._QAudios = []; } }; var Alias_AudioManager_stopAll = AudioManager.stopAll; AudioManager.stopAll = function() { Alias_AudioManager_stopAll.call(this); this.stopAllQAudio(); }; AudioManager.fadeOutQAudio = function(id, duration) { var buffers = this._QAudioBuffers; for (var i = buffers.length - 1; i >= 0; i--) { if (buffers[i] && buffers[i].uid === id) { buffers[i].fadeOut(duration); buffers[i] = null; buffers.splice(i, 1); $gameSystem._QAudios.splice(i, 1); break; } } }; AudioManager.fadeOutAllQAudio = function(duration) { var buffers = this._QAudioBuffers; for (var i = buffers.length - 1; i >= 0; i--) { buffers[i].fadeOut(duration); buffers[i] = null; buffers.splice(i, 1); $gameSystem._QAudios.splice(i, 1); } }; AudioManager.saveQAudios = function() { this._savedQAudios = $gameSystem._QAudios.clone(); }; AudioManager.restoreQAudios = function() { for (var i = 0; i < this._savedQAudios.length; i++) { var qAudio = this._savedQAudios[i]; this.playQAudio(qAudio.id, qAudio.audio, qAudio.options); } this._savedQAudios = []; }; AudioManager.checkForQAudios = function() { if (this._QAudioBuffers.length !== $gameSystem._QAudios.length) { var newAudios = $gameSystem._QAudios.clone(); this.stopAllQAudio(); for (var i = 0; i < newAudios.length; i++) { var qAudio = newAudios[i]; this.playQAudio(qAudio.id, qAudio.audio, qAudio.options); } } }; AudioManager.updateQAudio = function() { this.checkForQAudios(); var buffers = this._QAudioBuffers; for (var i = buffers.length - 1; i >= 0; i--) { if (!buffers[i]._autoPlay && !buffers[i].isPlaying()) { buffers[i].stop(); buffers.splice(i, 1); $gameSystem._QAudios.splice(i, 1); continue; } if (buffers[i].fadingIn) { this.updateQAudioFadeIn(buffers[i]); } this.updateQAudioDistance(buffers[i]); } }; AudioManager.updateQAudioFadeIn = function(buffer) { buffer.fadingIn.t++; buffer.rVolume = buffer.fadingIn.t / buffer.fadingIn.dur; if (buffer.fadingIn.t === buffer.fadingIn.dur) { buffer.fadingIn = null; } }; AudioManager.updateQAudioDistance = function(buffer) { var x1 = $gamePlayer._realX; var y1 = $gamePlayer._realY; var x2 = buffer.mapX; var y2 = buffer.mapY; if (buffer.bindTo !== undefined) { var chara = QPlus.getCharacter(buffer.bindTo); x2 = chara._realX; y2 = chara._realY; } if (buffer.cached) { if ((buffer.cached.x1 === x1 && buffer.cached.y1 === y1) && (buffer.cached.x2 === x2 && buffer.cached.y2 === y2) && (buffer.cached.rV === buffer.rVolume)) { return; } } var radius = buffer.radius; var radian = Math.atan2(y2 - y1, x2 - x1); var dx = $gameMap.deltaX(x2, x1); var dy = $gameMap.deltaY(y2, y1); var dist = Math.sqrt(dx * dx + dy * dy); var volume = Math.max((radius - dist) / radius, 0); var typeVolume = 100; if (buffer.type === 'bgm') typeVolume = this._bgmVolume; if (buffer.type === 'bgs') typeVolume = this._bgsVolume; if (buffer.type === 'me') typeVolume = this._meVolume; if (buffer.type === 'se') typeVolume = this._seVolume; volume *= buffer.maxVolume * (typeVolume / 100); buffer.volume = volume * buffer.rVolume; if (buffer.doPan) { var pan = Math.cos(radian); if (x2 === x1 && y2 === y1) { pan = 0; } buffer.pan = pan; } buffer.cached = { x1: x1, x2: x2, y1: y1, y2: y2, rV: buffer.rVolume } }; //----------------------------------------------------------------------------- // Scene_Base var Alias_Scene_Base_fadeOutAll = Scene_Base.prototype.fadeOutAll; Scene_Base.prototype.fadeOutAll = function() { Alias_Scene_Base_fadeOutAll.call(this); var time = this.slowFadeSpeed() / 60; AudioManager.fadeOutAllQAudio(time); }; //----------------------------------------------------------------------------- // Scene_Title var Alias_Scene_Title_playTitleMusic = Scene_Title.prototype.playTitleMusic; Scene_Title.prototype.playTitleMusic = function() { Alias_Scene_Title_playTitleMusic.call(this); AudioManager.stopAllQAudio(); }; //----------------------------------------------------------------------------- // Scene_Map var Alias_Scene_Map_update = Scene_Map.prototype.update; Scene_Map.prototype.update = function() { Alias_Scene_Map_update.call(this); AudioManager.updateQAudio(); }; var Alias_Scene_Map_stopAudioOnBattleStart = Scene_Map.prototype.stopAudioOnBattleStart; Scene_Map.prototype.stopAudioOnBattleStart = function() { Alias_Scene_Map_stopAudioOnBattleStart.call(this); AudioManager.saveQAudios(); AudioManager.stopAllQAudio(); }; //----------------------------------------------------------------------------- // BattleManager var Alias_BattleManager_replayBgmAndBgs = BattleManager.replayBgmAndBgs; BattleManager.replayBgmAndBgs = function() { Alias_BattleManager_replayBgmAndBgs.call(this); AudioManager.restoreQAudios(); }; //----------------------------------------------------------------------------- // Game_Map var Alias_Game_Map_setup = Game_Map.prototype.setup; Game_Map.prototype.setup = function(mapId) { AudioManager.stopAllQAudio(); Alias_Game_Map_setup.call(this, mapId); }; //----------------------------------------------------------------------------- // Game_System var Alias_Game_System_initialize = Game_System.prototype.initialize; Game_System.prototype.initialize = function() { Alias_Game_System_initialize.call(this); this._QAudios = []; }; })()