You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

632 line
18 KiB

  1. "use strict";
  2. var __awaiter = (this && this.__awaiter) || function(thisArg, _arguments, P, generator) {
  3. function adopt(value) {
  4. return value instanceof P ? value : new P(function(resolve) {
  5. resolve(value);
  6. });
  7. }
  8. return new(P || (P = Promise))(function(resolve, reject) {
  9. function fulfilled(value) {
  10. try {
  11. step(generator.next(value));
  12. } catch (e) {
  13. reject(e);
  14. }
  15. }
  16. function rejected(value) {
  17. try {
  18. step(generator["throw"](value));
  19. } catch (e) {
  20. reject(e);
  21. }
  22. }
  23. function step(result) {
  24. result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
  25. }
  26. step((generator = generator.apply(thisArg, _arguments || [])).next());
  27. });
  28. };
  29. Object.defineProperty(exports, "__esModule", {
  30. value: true
  31. });
  32. var zaudioCbName;
  33. (function(zaudioCbName) {
  34. zaudioCbName["onWaiting"] = "waiting";
  35. zaudioCbName["onError"] = "error";
  36. zaudioCbName["onTimeUpdate"] = "playing";
  37. zaudioCbName["onCanplay"] = "canPlay";
  38. zaudioCbName["onPause"] = "pause";
  39. zaudioCbName["onEnded"] = "ended";
  40. zaudioCbName["setAudio"] = "setAudio";
  41. zaudioCbName["updateAudio"] = "updateAudio";
  42. zaudioCbName["seek"] = "seek";
  43. zaudioCbName["onStop"] = "stop";
  44. zaudioCbName["syncStateOn"] = "syncStateOn";
  45. })(zaudioCbName || (zaudioCbName = {}));
  46. let zaudioCbNameArr = [];
  47. for (const key in zaudioCbName) {
  48. if (Object.prototype.hasOwnProperty.call(zaudioCbName, key)) {
  49. const item = zaudioCbName[key];
  50. zaudioCbNameArr.push(item);
  51. }
  52. }
  53. const util_1 = require("./util");
  54. /**
  55. * ZAudio类
  56. * @class ZAudio
  57. * @constructor
  58. * @param {String} defaultCover 音频默认封面
  59. * @param {Boolean} continuePlay 继续播放,错误播放或结束播放后执行
  60. * @param {Boolean} autoPlay 自动播放,部分浏览器不支持
  61. * @property {Number} renderIndex 当前渲染索引
  62. * @property {<audioinfo>} renderinfo 当前渲染数据
  63. * @property {Array<audio>} audiolist 音频列表数组
  64. * @property {<audioinfo>} playinfo 当前播放数据
  65. * @property {Boolean} paused 音频暂停状态
  66. * @property {Number} playIndex 当前播放索引
  67. * @property {Boolean} renderIsPlay 渲染与播放是否一致
  68. *
  69. * @method on(event, action, fn) 回调函数注册业务事件
  70. * @method off(event, action) 回调函数中卸载业务事件
  71. * @method setRender(data) 指定音频, 渲染到zaudio组件
  72. * @method syncRender() 同步并渲染当前的播放状态
  73. * @method operate(index) 播放或暂停指定索引的音频
  74. * @method setAudio(list) 覆盖音频列表
  75. * @method updateAudio(list) 添加音频列表
  76. * @method stop() 强制暂停当前播放音频
  77. * @method stepPlay(count) 快进快退
  78. * @method syncStateOn(action, cb) 注册一个用于同步获取当前播放状态的事件
  79. * @method syncStateOff(action) 卸载用于同步获取当前播放状态的事件
  80. *
  81. *
  82. * **/
  83. class ZAudio extends util_1.EventBus {
  84. constructor(options) {
  85. super();
  86. this.loading = false;
  87. this.renderIndex = 0;
  88. this.audiolist = [];
  89. this.renderinfo = {
  90. current: "00:00:00",
  91. duration: "00:00:00",
  92. duration_value: 0,
  93. current_value: 0,
  94. src: "",
  95. title: "",
  96. singer: "",
  97. coverImgUrl: "",
  98. };
  99. this.playinfo = {
  100. current: "00:00:00",
  101. duration: "00:00:00",
  102. duration_value: 0,
  103. current_value: 0,
  104. src: "",
  105. title: "",
  106. singer: "",
  107. coverImgUrl: "",
  108. };
  109. this.paused = true;
  110. this.uPause = false;
  111. this.autoPlay = false;
  112. this.defaultCover = "";
  113. this.continuePlay = true;
  114. //fix: 防抖触发音频播放中事件
  115. this.throttlePlaying = util_1.throttle(() => {
  116. this.emit(zaudioCbName.onTimeUpdate, this.playinfo);
  117. this.syncStateEmit();
  118. }, 1000);
  119. let {
  120. defaultCover,
  121. autoPlay,
  122. continuePlay
  123. } = options;
  124. this.defaultCover = defaultCover;
  125. this.autoPlay = autoPlay;
  126. this.continuePlay = continuePlay;
  127. this.init();
  128. }
  129. init() {
  130. // #ifndef H5
  131. var audioCtx = uni.getBackgroundAudioManager();
  132. // #endif
  133. // #ifdef H5
  134. var audioCtx = uni.createInnerAudioContext();
  135. audioCtx.autoplay = this.autoPlay;
  136. // #endif
  137. this.audioCtx = audioCtx;
  138. this.audioCtx.onWaiting(this.onWaitingHandler.bind(this));
  139. this.audioCtx.onCanplay(this.onCanplayHandler.bind(this));
  140. this.audioCtx.onPlay(this.onPlayHandler.bind(this));
  141. this.audioCtx.onPause(this.onPauseHandler.bind(this));
  142. this.audioCtx.onStop(this.onStopHandler.bind(this));
  143. this.audioCtx.onEnded(this.onEndedHandler.bind(this));
  144. this.audioCtx.onTimeUpdate(this.onTimeUpdateHandler.bind(this));
  145. this.audioCtx.onError(this.onErrorHandler.bind(this));
  146. //fix: 修复iOS原生音频切换不起作用
  147. // #ifdef APP-PLUS
  148. if (uni.getSystemInfoSync().platform == "ios") {
  149. const bgMusic = plus.audio.createPlayer();
  150. bgMusic.addEventListener("prev", () => {
  151. this.changeplay(-1);
  152. });
  153. bgMusic.addEventListener("next", () => {
  154. this.changeplay(1);
  155. });
  156. }
  157. // #endif
  158. // #ifndef H5
  159. setTimeout(() => {
  160. if (this.autoPlay) {
  161. this.operate();
  162. }
  163. }, 500);
  164. // #endif
  165. this.appCheckReplay();
  166. }
  167. //检测on off的参数
  168. checkEventParams(event, action, fn) {
  169. if (zaudioCbNameArr.indexOf(event) < 0) {
  170. console.error(`参数${event}错误, 必须为${zaudioCbNameArr.join(" | ")}中某一项`);
  171. return false;
  172. }
  173. if (typeof action !== "string" && typeof action !== "symbol") {
  174. console.error(`参数${action}错误, 参数必须为string或symbol类型`);
  175. return false;
  176. }
  177. if (fn && typeof fn !== "function") {
  178. console.error("fn参数错误");
  179. return false;
  180. }
  181. return true;
  182. }
  183. /**
  184. * @description 回调中卸载业务事件
  185. * @param {<zaudioCbName>} event 回调名称枚举值
  186. * @param {Sting|Symbol} action 业务函数名,用于区分不同业务
  187. * @returns undefined
  188. * **/
  189. off(event, action) {
  190. if (!this.checkEventParams(event, action))
  191. return;
  192. super.off(event, action);
  193. }
  194. /**
  195. * @description 回调中注册业务事件
  196. * @param {<zaudioCbName>} event 回调名称枚举值
  197. * @param {Sting|Symbol} action 业务函数名,用于区分不同业务
  198. * @param {function(object|string|number|undefined):undefined} fn 业务函数, 参数或为音频状态
  199. * @returns undefined
  200. * **/
  201. on(event, action, fn) {
  202. if (!this.checkEventParams(event, action))
  203. return;
  204. super.on(event, action, fn);
  205. }
  206. /**
  207. * @description 订阅触发音频回调
  208. * @param {<zaudioCbName>} event 回调名称枚举值,具体看zaudioCbName
  209. * @param {object|string|number|undefined} data 订阅触发回调时,传的音频属性
  210. * @returns undefined
  211. * **/
  212. emit(event, data) {
  213. super.emit(event, data);
  214. }
  215. commit(action, data) {
  216. typeof this[action] === "function" && this[action](data);
  217. }
  218. onWaitingHandler() {
  219. this.commit("setLoading", true);
  220. this.emit(zaudioCbName.onWaiting, true);
  221. this.syncStateEmit();
  222. }
  223. onCanplayHandler() {
  224. this.emit(zaudioCbName.onCanplay, this.playinfo);
  225. this.commit("setLoading", false);
  226. this.syncStateEmit();
  227. }
  228. onPlayHandler() {
  229. // #ifdef APP-PLUS
  230. this.commit("setPlayinfo", {
  231. duration: util_1.formatSeconds(this.audioCtx.duration),
  232. duration_value: this.audioCtx.duration,
  233. });
  234. // #endif
  235. this.commit("setPause", false);
  236. this.commit("setUnnormalPause", false);
  237. }
  238. onPauseHandler() {
  239. this.commit("setPause", true);
  240. this.emit(zaudioCbName.onPause);
  241. this.syncStateEmit();
  242. }
  243. onStopHandler() {
  244. this.commit("setPause", true);
  245. this.emit(zaudioCbName.onStop);
  246. this.syncStateEmit();
  247. }
  248. onEndedHandler() {
  249. this.commit("setPause", true);
  250. this.audioCtx.startTime = 0;
  251. this.commit("setPlayinfo", {
  252. current: "00:00:00",
  253. current_value: 0,
  254. src: "",
  255. });
  256. this.emit(zaudioCbName.onEnded);
  257. this.syncStateEmit();
  258. //续播
  259. if (this.continuePlay) {
  260. this.changeplay(1);
  261. } else {
  262. let nextkey = this.getNextKey(1);
  263. this.commit("setRender", nextkey);
  264. }
  265. }
  266. onTimeUpdateHandler() {
  267. if (this.renderIsPlay) {
  268. //fix: 解决播放进度大于总进度问题
  269. let currentTime = this.audioCtx.currentTime
  270. // let currentTime = this.audioCtx.currentTime > this.audioCtx.duration ?
  271. // this.audioCtx.duration :
  272. // this.audioCtx.currentTime;
  273. this.commit("setPlayinfo", {
  274. current: util_1.formatSeconds(currentTime),
  275. current_value: currentTime,
  276. });
  277. // #ifndef APP-PLUS
  278. //fix: 解决小程序与h5无法获取总进度的问题
  279. if (this.audioCtx.duration != this.playinfo.duration_value) {
  280. this.commit("setPlayinfo", {
  281. duration: util_1.formatSeconds(this.audioCtx.duration),
  282. duration_value: this.audioCtx.duration,
  283. });
  284. }
  285. // #endif
  286. }
  287. this.throttlePlaying();
  288. }
  289. onErrorHandler() {
  290. this.commit("setPause", true);
  291. this.commit("setRender", {
  292. src: "",
  293. title: "",
  294. singer: "",
  295. coverImgUrl: "",
  296. });
  297. this.commit("setPlayinfo", {
  298. current: "00:00:00",
  299. current_value: 0,
  300. duration: "00:00:00",
  301. duration_value: 0,
  302. title: "",
  303. src: "",
  304. });
  305. this.emit(zaudioCbName.onError);
  306. this.syncStateEmit();
  307. if (this.continuePlay) {
  308. this.changeplay(1);
  309. }
  310. }
  311. /**
  312. * @description 实时渲染当前状态
  313. * @returns undefined
  314. * **/
  315. syncRender() {
  316. this.setRender(this.playIndex);
  317. }
  318. /**
  319. * @description 注册一个实时获取ZAudio属性的方法
  320. * @param {String} action 自定义业务名
  321. * @param {Funtion} fn 实时获取ZAudio属性回调
  322. * @returns undefined
  323. * **/
  324. syncStateOn(action, fn) {
  325. typeof fn === "function" && this.on(zaudioCbName.syncStateOn, action, fn);
  326. }
  327. /**
  328. * @description 卸载实时获取ZAudio属性的方法
  329. * @param {String} action 自定义业务名
  330. * @returns undefined
  331. * **/
  332. syncStateOff(action) {
  333. this.off(zaudioCbName.syncStateOn, action);
  334. }
  335. /**
  336. * @description 订阅实时获取ZAudio属性的方法
  337. * @returns undefined
  338. * **/
  339. syncStateEmit() {
  340. this.emit(zaudioCbName.syncStateOn, {
  341. renderIndex: this.renderIndex,
  342. audiolist: this.audiolist,
  343. renderinfo: this.renderinfo,
  344. playinfo: this.playinfo,
  345. paused: this.paused,
  346. playIndex: this.playIndex,
  347. renderIsPlay: this.renderIsPlay,
  348. loading: this.loading,
  349. });
  350. }
  351. /**
  352. * @description 跳转播放
  353. * @param {Number} value 跳转位置
  354. * @returns undefined
  355. * **/
  356. seek(value) {
  357. let val = value > this.audioCtx.duration ? this.audioCtx.duration : value;
  358. this.audioCtx.seek(val);
  359. this.commit("setPlayinfo", {
  360. current: util_1.formatSeconds(val),
  361. current_value: val,
  362. });
  363. // setTimeout(() => {
  364. // this.emit(zaudioCbName.seek, this.playinfo.current);
  365. // }, 0);
  366. this.emit(zaudioCbName.seek, this.playinfo.current);
  367. }
  368. /**
  369. * @description 快进
  370. * @param {Number} value 跳转位置
  371. * @returns undefined
  372. * **/
  373. stepPlay(value) {
  374. if (this.renderIsPlay) {
  375. let pos = this.playinfo.current_value + value;
  376. this.seek(pos);
  377. }
  378. }
  379. /**
  380. * @description 获取下一首歌曲索引(用于渲染和播放)
  381. * @param {Number} count 切换数量
  382. * @returns number
  383. * **/
  384. getNextKey(count) {
  385. let nextkey = this.renderIndex;
  386. nextkey += count;
  387. nextkey =
  388. nextkey < 0 ?
  389. this.audiolist.length - 1 :
  390. nextkey > this.audiolist.length - 1 ?
  391. 0 :
  392. nextkey;
  393. return nextkey;
  394. }
  395. /**
  396. * @description 切歌
  397. * @param {Number} count 数量
  398. * @returns undefined
  399. * **/
  400. changeplay(count) {
  401. let nextkey = this.getNextKey(count);
  402. this.commit("setPause", true);
  403. this.operate(nextkey);
  404. }
  405. /**
  406. * @description 手动播放或暂停, 并渲染对应的数据
  407. * @param {Number|String|<audioInfo>|undefined} key 索引或音频对象
  408. * @returns undefined
  409. * **/
  410. operate(key) {
  411. key !== undefined && this.commit("setRender", key);
  412. this.operation();
  413. }
  414. /**
  415. * @description 强制暂停播放
  416. * @returns undefined
  417. * **/
  418. stop() {
  419. this.audioCtx.pause();
  420. this.commit("setPause", true);
  421. this.commit("setUnnormalPause", true);
  422. this.emit(zaudioCbName.onStop);
  423. }
  424. //播放,暂停事件判断,
  425. //播放数据与渲染数据相同时: 播放->暂停, 暂停->播放
  426. //播放数据与渲染数据不相同时: 播放渲染音频
  427. operation() {
  428. return __awaiter(this, void 0, void 0, function*() {
  429. const {
  430. duration,
  431. current,
  432. duration_value,
  433. current_value,
  434. src,
  435. } = this.playinfo;
  436. const {
  437. src: renderSrc,
  438. title: renderTitle,
  439. singer: renderSinger,
  440. coverImgUrl: renderCoverImgUrl,
  441. } = this.renderinfo;
  442. let renderIsPlay = this.renderIsPlay;
  443. let paused = this.paused;
  444. if (!renderIsPlay) {
  445. //渲染与播放地址 不同
  446. this.audioCtx.src = renderSrc;
  447. this.audioCtx.title = renderTitle;
  448. this.audioCtx.singer = renderSinger;
  449. this.audioCtx.coverImgUrl = renderCoverImgUrl || this.defaultCover;
  450. this.audioCtx.startTime = 0;
  451. // this.audioCtx.seek(0);
  452. // this.audioCtx.play();
  453. this.commit("setPause", false);
  454. this.commit("setPlayinfo", {
  455. src: renderSrc,
  456. title: renderTitle,
  457. singer: renderSinger,
  458. coverImgUrl: renderCoverImgUrl,
  459. });
  460. } else {
  461. if (paused) {
  462. //渲染与播放地址相同
  463. this.audioCtx.play();
  464. this.audioCtx.startTime = current_value;
  465. // this.audioCtx.seek(current_value);
  466. this.commit("setPause", false);
  467. this.commit("setPlayinfo", {
  468. src: renderSrc,
  469. title: renderTitle,
  470. singer: renderSinger,
  471. coverImgUrl: renderCoverImgUrl,
  472. });
  473. } else {
  474. this.audioCtx.pause();
  475. this.commit("setPause", true);
  476. this.commit("setUnnormalPause", true);
  477. }
  478. }
  479. });
  480. }
  481. /**
  482. * @description 覆盖音频
  483. * @param {Array<audio>} data 音频数组
  484. * @returns undefined
  485. * **/
  486. setAudio(data) {
  487. this.audiolist = [...data];
  488. this.emit(zaudioCbName.setAudio, this.audiolist);
  489. this.syncStateEmit();
  490. }
  491. /**
  492. * @description 添加音频
  493. * @param {Array<audio>} data 音频数组
  494. * @returns undefined
  495. * **/
  496. updateAudio(data) {
  497. this.audiolist.push(...data);
  498. this.emit(zaudioCbName.updateAudio, this.audiolist);
  499. this.syncStateEmit();
  500. }
  501. /**
  502. * @description 设置当前播放信息
  503. * @param {<audioInfo>} data 音频对象
  504. * @returns undefined
  505. * **/
  506. setPlayinfo(data) {
  507. for (let i in data) {
  508. this.playinfo[i] = data[i];
  509. }
  510. }
  511. /**
  512. * @description 设置暂停状态
  513. * @param {boolean} data 布尔值
  514. * @returns undefined
  515. * **/
  516. setPause(data) {
  517. this.paused = data;
  518. }
  519. /**
  520. * @description 设置loading
  521. * @param {boolean} data 布尔值
  522. * @returns undefined
  523. * **/
  524. setLoading(data) {
  525. this.loading = data;
  526. }
  527. /**
  528. * @description 设置通话时暂停状态
  529. * @param {boolean} data 布尔值
  530. * @returns undefined
  531. * **/
  532. setUnnormalPause(data) {
  533. this.uPause = data;
  534. }
  535. /**
  536. * @description 设置渲染
  537. * @param {number | string | audioInfo} data 索引或渲染信息
  538. * @returns undefined
  539. * **/
  540. setRender(data) {
  541. if (this.audiolist.length == 0)
  542. return;
  543. if (typeof data === "number" || typeof data === "string") {
  544. this.renderIndex = typeof data === "string" ? parseInt(data) : data;
  545. this.renderinfo = {
  546. src: this.audiolist[this.renderIndex].src,
  547. title: this.audiolist[this.renderIndex].title,
  548. singer: this.audiolist[this.renderIndex].singer,
  549. coverImgUrl: this.audiolist[this.renderIndex].coverImgUrl,
  550. current: "00:00:00",
  551. duration: "00:00:00",
  552. current_value: 0,
  553. duration_value: 100,
  554. };
  555. } else {
  556. this.renderinfo = data;
  557. let renderIndex = this.audiolist.findIndex((i) => i.src == data.src);
  558. if (renderIndex >= 0) {
  559. this.renderIndex = renderIndex;
  560. }
  561. }
  562. this.syncStateEmit();
  563. }
  564. //当前索引
  565. get playIndex() {
  566. let index = this.audiolist.findIndex((i) => i.src == this.playinfo.src);
  567. return index <= 0 ? 0 : index;
  568. }
  569. //渲染与播放是否一致
  570. get renderIsPlay() {
  571. return this.renderinfo.src == this.playinfo.src;
  572. }
  573. //app端判断电话来电后, 音频意外中断之后的继续播放
  574. appCheckReplay() {
  575. let _t = this;
  576. // #ifdef APP-PLUS
  577. try {
  578. if (uni.getSystemInfoSync().platform == "android") {
  579. var main = plus.android.runtimeMainActivity();
  580. var Context = plus.android.importClass("android.content.Context");
  581. var telephonyManager = plus.android.importClass("android.telephony.TelephonyManager");
  582. var telephonyManager = plus.android
  583. .runtimeMainActivity()
  584. .getSystemService(Context.TELEPHONY_SERVICE);
  585. var receiver = plus.android.implements("io.dcloud.android.content.BroadcastReceiver", {
  586. onReceive: function(intent) {
  587. //实现onReceiver回调函数
  588. plus.android.importClass(intent);
  589. var telephonyManager = plus.android.importClass(
  590. "android.telephony.TelephonyManager");
  591. var telephonyManager = plus.android
  592. .runtimeMainActivity()
  593. .getSystemService(Context.TELEPHONY_SERVICE);
  594. var phonetype = telephonyManager.getCallState();
  595. var phoneNumber = intent.getStringExtra(telephonyManager
  596. .EXTRA_INCOMING_NUMBER);
  597. if (phonetype == 0 && !_t.uPause) {
  598. _t.audioCtx.play();
  599. }
  600. },
  601. });
  602. var IntentFilter = plus.android.importClass("android.content.IntentFilter");
  603. var filter = new IntentFilter();
  604. filter.addAction(telephonyManager.ACTION_PHONE_STATE_CHANGED); //监听开关
  605. main.registerReceiver(receiver, filter); //注册监听
  606. } else if (uni.getSystemInfoSync().platform == "ios") {
  607. var callstatus = false;
  608. var CTCall = plus.ios.importClass("CTCall");
  609. var CTCallCenter = plus.ios.importClass("CTCallCenter");
  610. var center = new CTCallCenter();
  611. center.init();
  612. center.setCallEventr(function(ctCall) {
  613. callstatus = !callstatus;
  614. if (!callstatus && !_t.uPause) {
  615. _t.audioCtx.play();
  616. } else {
  617. _t.audioCtx.pause();
  618. }
  619. });
  620. }
  621. } catch (err) {
  622. console.warn(err);
  623. }
  624. // #endif
  625. }
  626. }
  627. exports.default = ZAudio;
  628. ZAudio.version = "2.2.51";