AI销管
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

zaudio.js 18 KiB

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