之前分享了一篇放烟花的源码文章,里面有一段关于音频播放的代码,看起来感觉挺优雅,于是在自己的答题小程序开发游戏音效的时候也借鉴了一下,在这里分享出来,希望可以对大家有所帮助和启发
wx.createWebAudioContext官方的文档链接在这里 https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/wx.createWebAudioContext.html
首先是封装一个音频处理器soundManager.js,这里面声明webAudio上下文、一些会使用到的音频资源(sources)和一些操作音频的方法,比如加载(preload)、播放(playSound)、暂停(pauseSound)
因为游戏音效一般是允许用户自己开关,因此还定义了静音类型,比如关闭游戏音乐是不播放背景音乐,关闭游戏音效则是不播放答对、答错、获胜、战败等音效
具体音频处理器的代码如下:
const api = require('api.js');
const soundManager = {
//静音缓存
muteCacheKey: 'SOUND.MUTE.',
//音频云储存文件基础路径
baseURL: 'https://7072-prod-xxxxxxx.tcb.qcloud.la/static/earn/sound/',
//WebAudio上下文
ctx: wx.createWebAudioContext(),
//音频资源
sources: {
bg: {
//音频文件名-背景音乐
filename: "bg.mp3",
//音量大小
volume: 1,
//音频的播放倍速,数值越大速度越快,默认速度1.0,有效范围为 0 < playbackRate <= 2.0(可读可写)
playbackRate: 1,
//是否循环播放
loop: true,
},
correct: {
//答对音效
filename: "correct.mp3",
volume: 1,
playbackRate: 1,
loop: false
},
wrong: {
//答错音效
filename: "wrong.mp3",
volume: 1,
playbackRate: 1,
loop: false
},
victory: {
//获胜音效
filename: "victory.mp3",
volume: 1,
playbackRate: 1,
loop: false
},
defeat: {
//战败音效
filename: "defeat.mp3",
volume: 1,
playbackRate: 1,
loop: false
}
},
/**
* 可静音声音类型
*/
muteTypes: {
background: ["bg"],
effect: ["correct", "wrong", "victory", "defeat"]
},
/**
* 预加载音频
* @returns {Promise[]>}
*/
preload() {
const allFilePromises = [];
console.warn("预加载音频");
/**
* 获取素材
* @param url
* @returns {Promise}
*/
function fetchMedia(url) {
return new Promise((resolve, reject) => {
wx.request({
url,
responseType: 'arraybuffer',
success: res => {
console.warn("获取素材成功:", url);
resolve(res);
},
fail: res => {
console.error('request fail', res)
reject();
}
})
})
}
/**
* 检查状态
* @param response
* @returns {*}
*/
function checkStatus(response) {
if (response.statusCode >= 200 && response.statusCode < 300) {
return response;
}
const customError = new Error(JSON.stringify(response));
customError.response = response;
throw customError;
}
const types = Object.keys(this.sources);
types.forEach(type => {
const source = this.sources[type];
const {filename} = source;
const fileURL = this.baseURL + filename;
// Promise will resolve with decoded audio buffer.
const promise = new Promise((resolve, reject) => {
fetchMedia(fileURL)
.then(checkStatus)
.then(res => {
//异步解码一段资源为AudioBuffer
this.ctx.decodeAudioData(res.data, buffer => {
source.buffer = buffer;
resolve(buffer);
}, res => {
console.warn("======>decodeAudioData fail:", res);
reject(res);
});
});
});
allFilePromises.push(promise);
});
return Promise.all(allFilePromises);
},
/**
* 播放
* @param type
* @param scale
*/
playSound(type, scale = 1) {
try {
console.warn("playSound:", type);
const source = this.sources[type];
if (!source) {
throw new Error(`Sound of type "${type}" doesn't exist.`);
}
if (this.muted(type)) {
console.warn("muteSound:", type);
return;
}
const initialVolume = source.volume || 1;
const initialPlaybackRate = source.playbackRate || 1;
// Volume descreases with scale.
const scaledVolume = initialVolume * scale;
// Playback rate increases with scale. For this, we map the scale of 0-1 to a scale of 2-1.
// So at a scale of 1, sound plays normally, but as scale approaches 0 speed approaches double.
const scaledPlaybackRate = initialPlaybackRate * (2 - scale);
//创建一个GainNode
const gainNode = this.ctx.createGain();
gainNode.gain.value = scaledVolume;
const {buffer} = source;
const bufferSource = this.ctx.createBufferSource();
bufferSource.playbackRate.value = scaledPlaybackRate;
bufferSource.buffer = buffer;
bufferSource.loop = source.loop;
bufferSource.connect(gainNode);
gainNode.connect(this.ctx.destination);
bufferSource.start(0);
source.bufferSource = bufferSource;
} catch (e) {
console.error("playSound error:", type);
}
},
/**
* 暂停播放
* @param type
*/
pauseSound(type) {
console.warn("pauseSound:", type);
const source = this.sources[type];
if (!source) {
throw new Error(`Sound of type "${type}" doesn't exist.`);
}
if (!source.bufferSource) {
console.warn("还未播放过");
return;
}
source.bufferSource.stop();
},
/**
* 修改静音状态
* @param muteType
* @param status true/false
*/
changeMuteStatus(muteType, status) {
const cacheKey = this.muteCacheKey + muteType;
if (status) {
api.setStorage(cacheKey, new Date().getTime());
} else {
api.removeStorage(cacheKey);
}
},
/**
* 获取静音状态
* @param muteType
* @returns {any|boolean}
*/
getMuteStatus(muteType) {
const result = api.getStorageSync(this.muteCacheKey + muteType);
return result && result !== "";
},
/**
* 是否处于静音
* @param type
* @returns {any|boolean}
*/
muted(type) {
for (let muteType in this.muteTypes) {
const muteTypeList = this.muteTypes[muteType];
const match = muteTypeList.findIndex(item => item === type) >= 0;
if (!match) {
continue;
}
return this.getMuteStatus(muteType);
}
return false;
}
}
module.exports = soundManager;
然后在app.js中的onLaunch加载音频资源
//预加载音频资源
soundManager.preload().then(res => console.log("音频资源预加载成功,在这里你可以之间播放比如背景音乐"));
这样需要播放音频的时候只需要这样,简简单单
const soundManager = require("utils/soundManager.js");
//播放背景音乐
soundManager.playSound("bg");
//播放答对音效
soundManager.playSound("correct");
//暂停背景音乐
soundManager.pauseSound("bg");