index.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. import { downloadPCM, downloadWAV, download } from './download/download';
  2. import { compress, encodePCM, encodeWAV } from './transform/transform';
  3. import Player from './player/player';
  4. import Recorder from './recorder/recorder';
  5. declare let window: any;
  6. declare let Math: any;
  7. declare let navigator: any;
  8. declare let Promise: any;
  9. // 构造函数参数格式
  10. interface recorderConfig {
  11. sampleBits?: number, // 采样位数
  12. sampleRate?: number, // 采样率
  13. numChannels?: number, // 声道数
  14. compiling?: boolean, // 是否边录边播
  15. }
  16. class Index extends Recorder {
  17. private isrecording: boolean = false; // 是否正在录音
  18. private ispause: boolean = false; // 是否是暂停
  19. private isplaying: boolean = false; // 是否正在播放
  20. public onplay: () => void; // 音频播放回调
  21. public onpauseplay: () => void; // 音频暂停回调
  22. public onresumeplay: () => void; // 音频恢复播放回调
  23. public onstopplay: () => void; // 音频停止播放回调
  24. public onplayend: () => void; // 音频正常播放结束
  25. /**
  26. * @param {Object} options 包含以下三个参数:
  27. * sampleBits,采样位数,一般8,16,默认16
  28. * sampleRate,采样率,一般 11025、16000、22050、24000、44100、48000,默认为浏览器自带的采样率
  29. * numChannels,声道,1或2
  30. */
  31. constructor(options: recorderConfig = {}) {
  32. super(options);
  33. }
  34. /**
  35. * 重新修改配置
  36. *
  37. * @param {recorderConfig} [options={}]
  38. * @memberof Recorder
  39. */
  40. public setOption(options: recorderConfig = {}) {
  41. this.setNewOption(options);
  42. }
  43. /**
  44. * Start the recording
  45. */
  46. start(): Promise<{}> {
  47. if (this.isrecording) {
  48. // 正在录音,则不允许
  49. return Promise.reject();
  50. }
  51. this.isrecording = true;
  52. return this.startRecord();
  53. }
  54. /**
  55. * Pause the recording
  56. */
  57. pause(): void {
  58. if (this.isrecording && !this.ispause) {
  59. this.ispause = true;
  60. // 当前不暂停的时候才可以暂停
  61. this.pauseRecord();
  62. }
  63. }
  64. /**
  65. * 继续录音
  66. */
  67. resume(): void {
  68. if (this.isrecording && this.ispause) {
  69. this.ispause = false;
  70. this.resumeRecord();
  71. }
  72. }
  73. /**
  74. * 停止录音
  75. *
  76. * @memberof Recorder
  77. */
  78. stop(): void {
  79. if (this.isrecording) {
  80. this.isrecording = false;
  81. this.ispause = false;
  82. this.stopRecord();
  83. }
  84. }
  85. /**
  86. * 播放录音
  87. */
  88. play(): void {
  89. this.stop();
  90. // 关闭前一次音频播放
  91. this.isplaying = true;
  92. this.onplay && this.onplay();
  93. Player.addPlayEnd(this.onplayend); // 注册播放完成后的回调事件
  94. const dataV = this.getWAV();
  95. if (dataV.byteLength > 44) {
  96. Player.play(dataV.buffer); // 播放
  97. }
  98. }
  99. /**
  100. * 获取已经播放了多长时间
  101. */
  102. getPlayTime(): number {
  103. return Player.getPlayTime();
  104. }
  105. /**
  106. * 暂停播放录音
  107. *
  108. * @memberof Recorder
  109. */
  110. pausePlay(): void {
  111. if (this.isrecording || !this.isplaying) {
  112. // 正在录音或没有播放,暂停无效
  113. return;
  114. }
  115. this.isplaying = false;
  116. this.onpauseplay && this.onpauseplay();
  117. Player.pausePlay();
  118. }
  119. /**
  120. * 恢复播放录音
  121. *
  122. * @memberof Recorder
  123. */
  124. resumePlay(): void {
  125. if (this.isrecording || this.isplaying) {
  126. // 正在录音或已经播放或没开始播放,恢复无效
  127. return;
  128. }
  129. this.isplaying = true;
  130. this.onresumeplay && this.onresumeplay();
  131. Player.resumePlay();
  132. }
  133. /**
  134. * 停止播放
  135. *
  136. * @memberof Recorder
  137. */
  138. stopPlay(): void {
  139. if (this.isrecording) {
  140. // 正在录音,停止录音播放无效
  141. return;
  142. }
  143. this.isplaying = false;
  144. this.onstopplay && this.onstopplay();
  145. Player.stopPlay();
  146. }
  147. destroy(): Promise<{}> {
  148. Player.destroyPlay();
  149. return this.destroyRecord();
  150. }
  151. /**
  152. * 获取当前已经录音的PCM音频数据
  153. *
  154. * @returns[DataView]
  155. * @memberof Recorder
  156. */
  157. // getWholeData() {
  158. // return this.tempPCM;
  159. // }
  160. /**
  161. * 获取余下的新数据,不包括 getNextData 前一次获取的数据
  162. *
  163. * @returns [DataView]
  164. * @memberof Recorder
  165. */
  166. // getNextData() {
  167. // let length = this.tempPCM.length,
  168. // data = this.tempPCM.slice(this.offset);
  169. // this.offset = length;
  170. // return data;
  171. // }
  172. /**
  173. * 获取当前录音的波形数据,
  174. * 调取频率由外部控制。
  175. *
  176. * @memberof Recorder
  177. */
  178. getRecordAnalyseData(): any {
  179. return this.getAnalyseData();
  180. }
  181. /**
  182. * 获取录音播放时的波形数据,
  183. *
  184. * @memberof Recorder
  185. */
  186. getPlayAnalyseData(): any {
  187. // 现在录音和播放不允许同时进行,所有复用的录音的analyser节点。
  188. return Player.getAnalyseData();
  189. }
  190. getPCM(): any {
  191. // 先停止
  192. this.stop();
  193. // 获取pcm数据
  194. let data: any = this.getData();
  195. // 根据输入输出比例 压缩或扩展
  196. data = compress(data, this.inputSampleRate, this.outputSampleRate);
  197. // 按采样位数重新编码
  198. return encodePCM(data, this.oututSampleBits, this.littleEdian);
  199. }
  200. /**
  201. * 获取PCM格式的blob数据
  202. *
  203. * @returns { blob } PCM格式的blob数据
  204. * @memberof Recorder
  205. */
  206. getPCMBlob(): any {
  207. return new Blob([ this.getPCM() ]);
  208. }
  209. /**
  210. * 下载录音pcm数据
  211. *
  212. * @param {string} [name='recorder'] 重命名的名字
  213. * @memberof Recorder
  214. */
  215. downloadPCM(name: string = 'recorder'): void {
  216. let pcmBlob = this.getPCMBlob();
  217. downloadPCM(pcmBlob, name);
  218. }
  219. /**
  220. * 获取WAV编码的二进制数据(dataview)
  221. *
  222. * @returns {dataview} WAV编码的二进制数据
  223. * @memberof Recorder
  224. */
  225. getWAV(): any {
  226. let pcmTemp = this.getPCM();
  227. // PCM增加44字节的头就是WAV格式了
  228. return encodeWAV(pcmTemp, this.inputSampleRate,
  229. this.outputSampleRate, this.config.numChannels, this.oututSampleBits, this.littleEdian);;
  230. }
  231. /**
  232. * 获取WAV音频的blob数据
  233. *
  234. * @returns { blob } wav格式blob数据
  235. * @memberof Recorder
  236. */
  237. getWAVBlob(): any {
  238. return new Blob([ this.getWAV() ], { type: 'audio/wav' });
  239. }
  240. /**
  241. * 下载录音的wav数据
  242. *
  243. * @param {string} [name='recorder'] 重命名的名字
  244. * @memberof Recorder
  245. */
  246. downloadWAV(name: string = 'recorder'): void {
  247. let wavBlob = this.getWAVBlob();
  248. downloadWAV(wavBlob, name);
  249. }
  250. /**
  251. * 通用的下载接口
  252. */
  253. download(blob, name: string, type: string): void {
  254. download(blob, name, type);
  255. }
  256. /**
  257. * 获取左和右声道的数据
  258. *
  259. * @returns [DataView]
  260. */
  261. getChannelData(): any {
  262. const all = this.getPCM();
  263. const length = all.byteLength;
  264. const littleEdian = this.littleEdian
  265. const res = { left: null, right: null }
  266. if (this.config.numChannels === 2) {
  267. // 双通道,劈开
  268. const lD = new DataView(new ArrayBuffer(length / 2))
  269. const rD = new DataView(new ArrayBuffer(length / 2))
  270. // 双声道,需要拆分下数据
  271. if (this.config.sampleBits === 16) {
  272. for (var i = 0; i < length / 2; i += 2) {
  273. lD.setInt16(i, all.getInt16(i * 2, littleEdian), littleEdian)
  274. rD.setInt16(i, all.getInt16(i * 2 + 2, littleEdian), littleEdian)
  275. }
  276. } else {
  277. for (var i = 0; i < length / 2; i += 2) {
  278. lD.setInt8(i, all.getInt8(i * 2))
  279. rD.setInt8(i, all.getInt8(i * 2 + 1))
  280. }
  281. }
  282. res.left = lD
  283. res.right = rD
  284. } else {
  285. // 单通道
  286. res.left = all
  287. }
  288. return res
  289. }
  290. }
  291. export default Index;