H5實現錄音,發送,播放,包含後端PHP接收並且存入數據庫源碼

代碼主要實現了集成騰訊雲Web IM , 就是一個網頁聊天,可以發送文字,語音,視頻通話,發送錄音。

但是騰訊雲的IM不支持錄音發送,所以自己加上了這個功能。下面請看效果圖和代碼。有相關的需求可以聯繫作者。

後端PHP源碼下載地址:

效果圖:

。。。

html代碼:

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
		<script src="./js/vconsole.min.js"></script>
		<link rel="stylesheet" href="./css/bootstrap-material-design.min.css">
		<link rel="stylesheet" href="./css/common.css">
		<link rel="stylesheet" href="./css/toastify.min.css">
		<script>
			// var vConsole = new VConsole();
		</script>
		<title>TRTC Web SDK Samples - 基礎音視頻通話</title>
		<!-- 引入 音視頻通話 TRTC WEB SDK 腳本 -->
		<meta name="keywords" content="醫療健康問答">
		<meta name="description" content="醫療健康問答">
		<meta name="apple-mobile-web-app-capable" content="yes">
		<meta name="apple-mobile-web-app-status-bar-style" content="black">
		<meta name="format-detection" content="telephone=no">
		<link rel="stylesheet" href="css/iconfont.css?v=231583415054">
		<link rel="stylesheet" href="css/animate.css?v=231583415054">
		<link rel="stylesheet" href="css/style.css?v=231583415054">
		<link rel="stylesheet" href="css/swiper.min.css?v=231583415054">
		<link rel="stylesheet" href="css/xhj.css?v=231583415054">
		<link rel="stylesheet" href="index.css">
		<script type="text/javascript" src="js/jquery.1.11.js?v=231583415054"></script>
		<style id="__WXWORK_INNER_SCROLLBAR_CSS">::-webkit-scrollbar { width: 12px !important; height: 12px !important; }::-webkit-scrollbar-track:vertical {  }::-webkit-scrollbar-thumb:vertical { background-color: rgba(136, 141, 152, 0.5) !important; border-radius: 10px !important; background-clip: content-box !important; border:2px solid transparent !important; } ::-webkit-scrollbar-track:horizontal {  }::-webkit-scrollbar-thumb:horizontal { background-color: rgba(136, 141, 152, 0.5) !important; border-radius: 10px !important; background-clip: content-box !important; border:2px solid transparent !important; } ::-webkit-resizer { display: none !important; }</style>
		<script type="text/javascript" src="js/swiper.min.js?v=231583415054"></script>
		<script type="text/javascript" src="js/xhj.js?v=231583415054"></script>
		<script type="text/javascript">
			var tpl_src = "/Public/web";
		</script>
		<link rel="stylesheet" href="css/iconfont.css?v=231583415054">
		<link rel="stylesheet" href="js/remodal.css">
		<script src="js/remodal.js"></script>
		<link rel="stylesheet" href="css/ask.css?v=231583415054">
		<style type="text/css">
			.send_block_item_voiceBg{
				font-size: 15px;
				border: 1px solid #009688;
				height: 4rem;
				line-height: 4rem;
				text-align: center;
				margin: 1.575rem;
				width: 12.5rem;
				border-radius: 10px;
				background-color: #00A4FF;
				color: white;
			}
			.send_block {
				opacity: 1;
			}

			.send_block_item {
				display: flex;
				flex-direction: column;
			}

			.send_block_item img {
				width: 2.5rem;
			}

			.cctbt {
				margin-top: -30px;
			}
		</style>
	</head>
	<body style="overflow: hidden; background:#eee">
		<div id="page" class="page">

			<div v-if="openImgShow" @click="openImg('hide')" class="send_block_bg"></div>
			<div v-if="openImgShow">
				<img class="openImgUrl" :src="openimgsrc"></div>
			<div v-if="audio_start_status" class="send_block_bg"></div>
			
			<div v-if="audio_send_status" class="jietingzhong">
				<div class="jietingTxt">上傳中...</div>
			</div>
			
			<div v-if="audio_start_status">
				<img class="openImgUrl" src="./images/video.png"></div>

			<div v-if="jietingzhong" class="jietingzhong">
				<div class="send_block_bg">
				</div>
				<img class="jietingImg" src="./images/jieting.png">
				<div class="jietingTxt">等待對方接聽 ......</div>
			</div>
			<div v-if="upStatus" class="jietingzhong">
				<div class="send_block_bg">
				</div>
				<img class="jietingImg" src="./images/jieting.png">
				<div class="jietingTxt">上傳中 ......</div>
			</div>
			<div class="video-grid" id="video_grid">
				<div id="local_stream" class="video-placeholder">
					<div id="local_video_info" class="video-info"></div>
				</div>
			</div>
			<div v-if="cameras&&video_ing" class="video_btn">
				<div class="video_btn_item" @click="qiehuan">切換攝像頭</div>
				<div class="video_btn_item" @click="guaduan">掛斷</div>
			</div>
			<div class="header ct"> <a @click="outVideo" class="return"></a>
				<h1>回覆提問者 always 問題{{txtMsg}}<span></span></h1>

			</div>
			<div class="chatbox">
				<div class="prl20">
					<ul style="overflow:auto;" class="chat-list" ref="chatContent">
						<div v-for="(item,idx) in messageList">
							<!-- 右邊 -->
							<li class="r" v-if="item.from==im_id">
								<p class="ct-time" v-if="item.time">{{item.my_time}}</p>
								<div class="ct-item">
									<div class="ct-head"><img :src="u_hrd_img"></div>
									<!-- 文本 -->
									<div class="ct-text" @click="clickTxt(item.payload.text)" v-if='item._elements[0].type=="TIMTextElem"'>{{item.payload.text}}
										<p class="video_txt" v-if='item.payload.text=="發起視頻聊天邀請"'></p>
									</div>
									<!-- 圖片 -->
									<div v-if='item._elements[0].type=="TIMImageElem"' class="ct-text im1">
										<img @click="openImg" :src="item._elements[0].content.imageInfoArray[0].imageUrl" />
									</div>
									<!-- 文件 fileName fileUrl-->
									<div v-if='item._elements[0].type=="TIMFileElem"'>
										<div class="ct-text" @click="openFile">文件:{{item.payload.fileName}}</div>
										<div v-if="fileShow" @click="openFile" class="send_block_bg"></div>
										<iframe v-show="fileShow" class="filename" :src="item.payload.fileUrl" frameborder='1'></iframe>
									</div>
								</div>
							</li>
							<!-- 左邊 -->
							<li v-else>
								<p class="ct-time">{{item.my_time}}</p>
								<div class="ct-item">
									<div class="ct-head"><img :src="t_u_hrd_img"></div>
									<!-- 文本 -->
									<div v-if='item._elements[0].type=="TIMTextElem"' @click="clickTxt(item.payload.text)" class="ct-text">
										{{item.payload.text}}
										<p class="video_txt" v-if='item.payload.text=="發起視頻聊天邀請"&&idx==messageList.length-1'><span @click="sendVideo(2)">接受
											</span><span @click="refuseVideo()" style="margin-left: 25px;"> 拒絕</span></p>

									</div>

									<!-- 圖片 -->
									<div v-if='item._elements[0].type=="TIMImageElem"' class="ct-text im1">
										<img @click="openImg" :src="item._elements[0].content.imageInfoArray[0].imageUrl">
									</div>

									<!-- 文件 fileName fileUrl-->
									<div v-if='item._elements[0].type=="TIMFileElem"'>
										<div class="ct-text" @click="openFile">文件:{{item.payload.fileName}}</div>
										<div v-if="fileShow" @click="openFile" class="send_block_bg"></div>
										<div v-if="fileShow">
											<iframe class="filename" :src="item.payload.fileUrl" frameborder='1'></iframe></div>
									</div>
								</div>
							</li>
						</div>
					</ul><!-- 輸入框 -->


					<div style="height:52px;"></div>
				</div>
			</div>
			<div v-if="send_block_show" @click="send_block_show=false,show_audio=false" class="send_block_bg">
			</div>
			<div v-if="send_block_show" class="send_block">
				<div v-if="show_audio" @click="audio_start()" class=" send_block_item_voiceBg">點擊開始錄音</div>
				<div v-if="show_audio" @click="audio_end()" class=" send_block_item_voiceBg">結束錄音併發送</div>



				<div v-if="!show_audio" class="send_block_item" @click="sendImg">
					<div> <img src="./images/51db2326125cf77a37f502f51794980.png"> </div>
					<div class="cctbt">發送圖片</div>
				</div>
				<div v-if="!show_audio" class="send_block_item" @click="show_audio_click()">
					<div>
						<img src="images/video.png"></div>
					<div class="cctbt">發送錄音</div>
				</div>
				<div v-if="!show_audio" class="send_block_item" @click="sendVideo(2)">
					<div>
						<img src="images/6e6ef015ae1c8c7903c968f4cf88856.png"></div>
					<div class="cctbt">視頻聊天</div>
				</div>
			</div>

			<div class="chat-input fxied" v-show="!send_block_show">
				<div class="chat-input-top">
					<div class="ct-top"> <span class="ctadd" @click="send_block_show_click"><img src="images/add.png" alt=""></span>
						<!-- <div class="textarea kj_re_title" contenteditable="true"></div> -->
						<!-- <textarea class="textarea kj_re_title" v-model="txtMsg" ></textarea> -->
							
						<!-- <div class="textarea" contenteditable="true" v-html="txtMsg" @input="changeText"></div> -->
						
						<div class="textarea" id="textarea" contenteditable="true" v-html="txtMsg" @blur="changeText"></div>

						<button class="ctbtn btn_submit_re_title" v-on:click="sendTxt">發送</button>
					</div>
				</div>
			</div>
			<input type="file" accept="image/*" id="input_img" multiple style="display: none" @change="ImgFileChange">
			<input type="file" id="input_file" style="display: none" @change="fileChange">

		</div>

		<script src="./js/jquery-3.2.1.min.js"></script>
		<script src="./js/popper.js"></script>
		<script src="./js/toastify.js"></script>
		<script src="./js/bootstrap-material-design.min.js"></script>
		<script>
			$(document).ready(function() {
				$('body').bootstrapMaterialDesign();
			});
		</script>
		<!-- 引入 TRTC WEB SDK 腳本 -->
		<script src="./js/trtc.js"></script>
		<!-- Demo 相關腳本 -->
		<script src="./js/lib-generate-test-usersig.min.js"></script>
		<script src="./js/debug/GenerateTestUserSig.js"></script>
		<script src="./js/utils.js"></script>
		<script src="./js/rtc-client.js"></script>
		<script src="./js/tim-js.js"></script>
		<script src="./js/cos-js-sdk-v5.min.js"></script>
		<script src="./index.js"></script>
		<script src="https://cdn.jsdelivr.net/npm/vue"></script>
		<link rel="shortcut icon" type="image/png" href="https://cdn.jsdelivr.net/gh/xiangyuecn/Recorder@latest/assets/icon.png">
		<script src="https://cdn.jsdelivr.net/gh/xiangyuecn/Recorder@latest/recorder.mp3.min.js"></script>
		<script>
			var rec = Recorder();
			var app = new Vue({
				el: '#page',
				data: {
					show_audio: false,
					audio_src_autoplay:"",
					audio_src:"",
					t_u_hrd_img: GetRequest().t_u_hrd_img,
					u_hrd_img: GetRequest().u_hrd_img,
					jietingzhong: false,
					messageList: [],
					upStatus: false,
					video_idx: 0,
					cameras: null,
					im_id: userID,
					video_ing: false,
					fileShow: false,
					send_block_show: false,
					openImgShow: '',
					openimgsrc: '',
					txtMsg: "",
					audio_send_status:false,
					audio_start_status:false,
					audio_start_time: 0,
					audio_ent_time: 0
				},
				watch: {
					messageList() {
						console.log("messageList change");
						this.$nextTick(() => {
							var h = $(".chat-list").innerHeight();
							$(".chatbox").scrollTop(h);
						})
					}
				},
				methods: {
					changeText(e){
						console.log('------',e.target.innerHTML)
						// this.txtMsg =e.target.innerHTML;
						this.txtMsg=e.target.innerText
					},
					show_audio_click() {
						this.show_audio = true;
					},
					clickTxt(txt) {
						console.log('txt', txt)
						if (txt.indexOf("錄音-") != -1) {

							this.audio_play(txt);
						}
					},
					audio_start() {
						console.log('開始錄音')
						this.audio_start_status=true;
						this.audio_start_time = new Date().getTime();
						rec.open(function() {
							rec.start();
						})

					},
					audio_play(txt) {
						let aa = txt.split("錄音-")[1]
						let url = "https://jayjing.wang/audio/upload/" + aa + ".mp3";
						console.log('播放錄音',url)
						let audio = new Audio()
						    audio.src = url;
						    audio.play();
					},
					audio_end(status) {
						this.audio_start_status=false;
						console.log('停止錄音', status)
						this.show_audio = false;
						this.audio_ent_time = new Date().getTime();
						if (this.audio_ent_time - this.audio_start_time < 1000) {
							Toast.notify('說話不能小於1秒 - ')
							return
						}

						rec.stop(function(blob, duration) {
								Toast.notify('上傳中···')
							
							this.audio_send_status = true;
							/***方式二:使用FormData用multipart/form-data表單上傳文件***/
							var form = new FormData();
							let nowTimeId = new Date().getTime()
							form.append("file", blob, nowTimeId + ".mp3"); //和普通form表單並無二致,後端接收到upfile參數的文件,文件名爲recorder.mp3
							form.append("id", nowTimeId);
							//...其他表單參數
							$.ajax({
								url: 'https://jayjing.wang/audio/up.php',
								type: "POST",
								contentType: false //讓xhr自動處理Content-Type header,multipart/form-data需要生成隨機的boundary
									,
								processData: false //不要處理data,讓xhr自動處理
									,
								data: form,
								success: function(v) {
									this.audio_send_status = false;
									console.log("上傳成功", v);
									that.sendAudio(nowTimeId);
								},
								error: function(s) {
									this.audio_send_status = false;
									console.error("上傳失敗", s);
								}
							});

							//-----↑↑↑以上纔是主要代碼↑↑↑-------
						}, function(msg) {
							console.log("錄音失敗:" + msg);
						});
					},
					sendAudio(nowTimeId) {
						console.log('發送錄音')
						let txt = '錄音-' + nowTimeId;
						this.sendTxt(txt);
					},
					qiehuan() {
						if (this.cameras) {
							// 切換攝像頭
							let cameraId = this.cameras[this.video_idx].deviceId;
							rtc.localStream_.switchDevice('video', cameraId).then(() => {
								console.log('switch camera success', cameraId);
								if (this.video_idx < this.cameras.length - 1) {
									this.video_idx = this.video_idx + 1
								} else {
									this.video_idx = 0
								}
							});
						}
					},
					guaduan() {
						rtc.leave();
						this.video_ing = false;
						this.jietingzhong = false
						rtc = null;
					},
					outVideo() {
						location.reload();
					},
					refuseVideo() {
						let txt = '未接聽';
						this.sendTxt(txt);
					},
					sendVideo(e) {
						console.log('11111111', e)
						this.txtMsg = "";
						if (rtc) return;
						rtc = new RtcClient({
							userId: userID,
							roomId: roomID,
							sdkAppId,
							userSig: userSig
						});
						rtc.join().then(() => {
							if (rtc.isJoined_) {
								let txt = '發起視頻聊天邀請';
								this.sendTxt(txt);
								Toast.notify('等待接聽 - ')
								this.jietingzhong = true;
								this.video_ing = true;

								// 遠端用戶進房
								rtc.client_.on('peer-join', evt => {
									this.jietingzhong = false;
								})
								// 遠端用戶退房
								rtc.client_.on('peer-leave', evt => {
									console.log('========================================')
									this.video_ing = false;
									this.send_block_show = false;
									rtc.leave();
									rtc = null
									location.reload()
								})
							} else {
								this.video_ing = false;
							}
							this.send_block_show = false;
							TRTC.getCameras().then(devices => {
								this.cameras = devices;

							});
						});
						console.log('rtc------------------------------', rtc);
					},
					send_block_show_click() {
						this.send_block_show = true;
					},
					// 發送消息
					sendMsg(message) {
						this.txtMsg = "";
						this.send_block_show = false;
						let promise = tim.sendMessage(message);
						promise.then(function(msg) {
							// 發送成功
							console.log("發送成功", msg);
							that.setMessageList(msg.data.message)
						}).catch(function(imError) {
							// 發送失敗
							console.warn('sendMessage error:', imError);
						});
					},
					openImg(e) {
						console.log(e)
						if (e == "hide") {
							this.openImgShow = false;
						} else {
							this.openImgShow = true;
							this.openimgsrc = e.target.currentSrc;
						}
					},
					// 打開文件
					openFile() {
						console.log('打開文件')
						this.fileShow = !this.fileShow;
						// $('#see_file').click();
					},
					// 發送文件
					sendFile() {
						console.log('端發送圖片消息')
						$('#input_file').click();
					},
					fileChange(e) {
						this.upStatus = true;
						// Web 端發送文件消息示例1 - 傳入 DOM 節點
						// 1. 創建文件消息實例,接口返回的實例可以上屏
						let message = tim.createFileMessage({
							to: to_user,
							conversationType: TIM.TYPES.CONV_C2C,
							// 消息優先級,用於羣聊(v2.4.2起支持)。如果某個羣的消息超過了頻率限制,後臺會優先下發高優先級的消息,詳細請參考:https://cloud.tencent.com/document/product/269/3663#.E6.B6.88.E6.81.AF.E4.BC.98.E5.85.88.E7.BA.A7.E4.B8.8E.E9.A2.91.E7.8E.87.E6.8E.A7.E5.88.B6)
							// 支持的枚舉值:TIM.TYPES.MSG_PRIORITY_HIGH, TIM.TYPES.MSG_PRIORITY_NORMAL(默認), TIM.TYPES.MSG_PRIORITY_LOW, TIM.TYPES.MSG_PRIORITY_LOWEST
							// priority: TIM.TYPES.MSG_PRIORITY_NORMAL,
							payload: {
								file: document.getElementById('input_file'),
							},
							onProgress: (event) => {
								if (event == 1) {
									this.upStatus = false
								}
								console.log('file uploading:', event)
							}
						});
						this.sendMsg(message)

					},
					// 發送圖片
					ImgFileChange(e) {
						this.upStatus = true;
						console.log('eeeee', e, document.getElementById('input_img'))
						// Web 端發送圖片消息
						// 1. 創建消息實例,接口返回的實例可以上屏
						let message = tim.createImageMessage({
							to: to_user,
							conversationType: TIM.TYPES.CONV_C2C,
							payload: {
								file: document.getElementById('input_img'),
							},
							onProgress: (event) => {
								if (event == 1) {
									this.upStatus = false
								}
								console.log('------------file uploading:', event)
							}
						});
						this.sendMsg(message)
					},
					sendImg() {
						console.log('端發送圖片消息')
						$('#input_img').click();
					},
					// 獲取消息列表
					getList() {
						// 打開某個會話時,第一次拉取消息列表
						tim.getMessageList({
							conversationID: conversationID,
							count: 15
						}).then(function(imResponse) {
							console.log('----------第一次拉取消息列表', imResponse)
							let list = imResponse.data.messageList;
							list.forEach((item, idx) => {
								if (idx != 0) {
									// console.log('item.time-list[idx-1].time', item.time - list[idx - 1].time)
									if (item.time - list[idx - 1].time > 500) {
										item.my_time = timestampToTime(item.time);
									} else {
										item.my_time = ""
									}
								}
								if (idx == 0) {
									item.my_time = timestampToTime(item.time);
								}
							})
							that.messageList = imResponse.data.messageList; // 消息列表。

							// 下拉查看更多消息
							// let promise = tim.getMessageList({ conversationID: conversationID, nextReqMessageID, count: 15 });
							// promise.then(function (imResponse) {
							//   const messageList = imResponse.data.messageList; // 消息列表。
							//   const nextReqMessageID = imResponse.data.nextReqMessageID; // 用於續拉,分頁續拉時需傳入該字段。
							//   const isCompleted = imResponse.data.isCompleted; // 表示是否已經拉完所有消息。
							// });
						});

					},
					setMessageList(obj) {

						let idx = this.messageList.length;
						obj.idx = idx;
						that.messageList.push(obj)
						console.log('添加聊天列表數據', that.messageList)
					},
					sendTxt(txt = "") {
						console.log('+++++++++',$("#textarea").val())
						console.log('this.txtMsg:',this.txtMsg)
						let message = tim.createTextMessage({
							to: to_user,
							conversationType: TIM.TYPES.CONV_C2C,
							payload: {
								text: this.txtMsg ? this.txtMsg : txt
							}
						});
						this.sendMsg(message)
					},
					im_on() {
						// 監聽事件,例如:
						tim.on(TIM.EVENT.SDK_READY, function(event) {
							console.log('-------收到離線消息和會話列表同步完畢通知')
							that.getList()
						});

						tim.on(TIM.EVENT.MESSAGE_RECEIVED, (event) => {
							console.log('-------收到推送的單聊、羣聊、羣提示、羣系統通知的新消息', event)
							if (event.data[0].payload.text == "未接聽") {
								rtc.leave();
								this.video_ing = false;
								this.jietingzhong = false
							}
							that.setMessageList(event.data[0])
						});

						tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, function(event) {
							console.log('-------收到會話列表更新通知', event)
						});
					},
				},
				mounted() {
					that = this;

					let host = window.location.protocol + "//" + window.location.host;
					console.log('------------------', host)
					this.im_on();

				}
			})
		</script>

	</body>
</html>

JS代碼:

var that;
var sdkAppId = 1400325026;
var options = {
	SDKAppID: sdkAppId // 接入時需要將0替換爲您的即時通信 IM 應用的 SDKAppID
};
let rtc = null;

var to_user = "user_1959"
// var to_user = "user_24384277"


// var userID = "user_25"
// var userSig = "eJyrVgrxCdYrSy1SslIy0jNQ0gHzM1NS80oy0zLBwqXFqUXxRqZQqeKU7MSCgswUJStDEwMDYyNTAyMziExJZm4qUNTUwtjCwMjIFCqaWlGQWQQUNzMwsTAwgJqRmQ4016ugICsoQDsoOcM0NSqroNIn2Dkn19AyKMMzudDfwLQg2zcxPF8-ysXX09FWqRYAKGQxhQ__"

var userID = "user_3n"
var userSig ="eJwtzFELgjAYheH-suuwr*lsCF2uEIsRSVA3EbnGVzjmXBlG-72hXp7nwPsl5fYQvZUjGaERkNmwsVLG4x0HfrXKXWIzXW31vFqLFckWCUBMGdB0fDzWKijjwQESNqr6WHTBU0g4wNRAHbq84U1ZHHPXz8W6r0W*lC63Rac7quX*ITdn9Cdxk8buVuT3Bz4RMjE_";

// var userID = "user_86943595"
// var userSig = "eJwtzM0KgkAUBeB3mW2htxnvOAotWvRHEkWptYrQUS6SmZok0bsn6vJ853C*7OydjEaXzGXcADbtM8U6rymhnt*VLm9KOpZAB8dBFWf3oqCYuTMLQHAELoempofuFJVQQiDgoPpTUNm5BEsBjB*Udu-HZbuixheL4BLxDUXb9uXBWibhzs78aBJcD2ZuYkp7*zlnvz8LnzI5"


var roomID = "889974741"

var conversationID = "C2C" + to_user;
var tim = TIM.create(options);
var pageImResponse;
tim.setLogLevel(0); // 普通級別,日誌量較多,接入時建議使用
tim.registerPlugin({
	'cos-js-sdk': COS
});
let promise = tim.login({
	userID,
	userSig
}).then(function(imResponse) {
	pageImResponse = imResponse;
	console.log('登錄成功', imResponse.data); // 登錄成功
}).catch(function(imError) {
	console.warn('login error:', imError); // 登錄失敗的相關信息
});
// let trc_Data = {
// 	mode: 'videoCall',
// 	sdkAppId,
// 	userId: userID,
// 	userSig
// }
// console.log('----------------------trc_Data ', trc_Data);

//時間戳轉日期格式
function timestampToTime(timestamp) {
	var date = new Date(timestamp * 1000); //時間戳爲10位需*1000,時間戳爲13位的話不需乘1000
	Y = date.getFullYear() + '-';
	M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
	D = date.getDate() + ' ';
	h = date.getHours() + ':';
	m = date.getMinutes() + ':';
	s = date.getSeconds();
	return Y + M + D + h + m + s;
}
//獲取url中參數
function GetRequest()
 {
    var url = location.search;   //獲取url中"?"符後的字串   
    var theRequest = new Object();
    if (url.indexOf("?") != -1) 
    {
        var str = url.substr(1);
        strs = str.split("&");
        for (var i = 0; i < strs.length; i++) 
        {
            theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
        }
    }
    return theRequest;
 }

引用的 audio.js 代碼:

var HZRecorder = function (stream, config) {  
    config = config || {};  
    config.sampleBits = config.sampleBits || 8;      //採樣數位 8, 16  
    config.sampleRate = config.sampleRate || (44100 / 6);   //採樣率(1/6 44100)  
 
      
    //創建一個音頻環境對象  
    audioContext = window.AudioContext || window.webkitAudioContext;  
    var context = new audioContext();  
 
    //將聲音輸入這個對像  
    var audioInput = context.createMediaStreamSource(stream);  
      
    //設置音量節點  
    var volume = context.createGain();  
    audioInput.connect(volume);  
 
    //創建緩存,用來緩存聲音  
    var bufferSize = 4096;  
 
    // 創建聲音的緩存節點,createScriptProcessor方法的  
    // 第二個和第三個參數指的是輸入和輸出都是雙聲道。  
    var recorder = context.createScriptProcessor(bufferSize, 2, 2);  
 
    var audioData = {  
        size: 0          //錄音文件長度  
        , buffer: []     //錄音緩存  
        , inputSampleRate: context.sampleRate    //輸入採樣率  
        , inputSampleBits: 16       //輸入採樣數位 8, 16  
        , outputSampleRate: config.sampleRate    //輸出採樣率  
        , oututSampleBits: config.sampleBits       //輸出採樣數位 8, 16  
        , input: function (data) {  
            this.buffer.push(new Float32Array(data));  
            this.size += data.length;  
        }  
        , compress: function () { //合併壓縮  
            //合併  
            var data = new Float32Array(this.size);  
            var offset = 0;  
            for (var i = 0; i < this.buffer.length; i++) {  
                data.set(this.buffer[i], offset);  
                offset += this.buffer[i].length;  
            }  
            //壓縮  
            var compression = parseInt(this.inputSampleRate / this.outputSampleRate);  
            var length = data.length / compression;  
            var result = new Float32Array(length);  
            var index = 0, j = 0;  
            while (index < length) {  
                result[index] = data[j];  
                j += compression;  
                index++;  
            }  
            return result;  
        }  
        , encodeWAV: function () {  
            var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);  
            var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);  
            var bytes = this.compress();  
            var dataLength = bytes.length * (sampleBits / 8);  
            var buffer = new ArrayBuffer(44 + dataLength);  
            var data = new DataView(buffer);  
 
            var channelCount = 1;//單聲道  
            var offset = 0;  
 
            var writeString = function (str) {  
                for (var i = 0; i < str.length; i++) {  
                    data.setUint8(offset + i, str.charCodeAt(i));  
                }  
            };  
              
            // 資源交換文件標識符   
            writeString('RIFF'); offset += 4;  
            // 下個地址開始到文件尾總字節數,即文件大小-8   
            data.setUint32(offset, 36 + dataLength, true); offset += 4;  
            // WAV文件標誌  
            writeString('WAVE'); offset += 4;  
            // 波形格式標誌   
            writeString('fmt '); offset += 4;  
            // 過濾字節,一般爲 0x10 = 16   
            data.setUint32(offset, 16, true); offset += 4;  
            // 格式類別 (PCM形式採樣數據)   
            data.setUint16(offset, 1, true); offset += 2;  
            // 通道數   
            data.setUint16(offset, channelCount, true); offset += 2;  
            // 採樣率,每秒樣本數,表示每個通道的播放速度   
            data.setUint32(offset, sampleRate, true); offset += 4;  
            // 波形數據傳輸率 (每秒平均字節數) 單聲道×每秒數據位數×每樣本數據位/8   
            data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;  
            // 快數據調整數 採樣一次佔用字節數 單聲道×每樣本的數據位數/8   
            data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;  
            // 每樣本數據位數   
            data.setUint16(offset, sampleBits, true); offset += 2;  
            // 數據標識符   
            writeString('data'); offset += 4;  
            // 採樣數據總數,即數據總大小-44   
            data.setUint32(offset, dataLength, true); offset += 4;  
            // 寫入採樣數據   
            if (sampleBits === 8) {  
                for (var i = 0; i < bytes.length; i++, offset++) {  
                    var s = Math.max(-1, Math.min(1, bytes[i]));  
                    var val = s < 0 ? s * 0x8000 : s * 0x7FFF;  
                    val = parseInt(255 / (65535 / (val + 32768)));  
                    data.setInt8(offset, val, true);  
                }  
            } else {  
                for (var i = 0; i < bytes.length; i++, offset += 2) {  
                    var s = Math.max(-1, Math.min(1, bytes[i]));  
                    data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);  
                }  
            }  
 
            return new Blob([data], { type: 'audio/wav' });  
        }  
    };  
 
    //開始錄音  
    this.start = function () {  
        audioInput.connect(recorder);  
        recorder.connect(context.destination);  
    };  
 
    //停止  
    this.stop = function () {  
        recorder.disconnect();  
    };  
    
    // 結束
    this.end = function() {
        context.close();
    };
    
    // 繼續
    this.again = function() {
        recorder.connect(context.destination);
    };
 
    //獲取音頻文件  
    this.getBlob = function () {  
        this.stop();  
        return audioData.encodeWAV();  
    };  
 
    //回放  
    this.play = function (audio) {  
        audio.src = window.URL.createObjectURL(this.getBlob());  
    };  
 
    //上傳  
    this.upload = function (url, callback) {  
        var fd = new FormData();  
        fd.append('audioData', this.getBlob());  
        var xhr = new XMLHttpRequest();  
        if (callback) {  
            xhr.upload.addEventListener('progress', function (e) {  
                callback('uploading', e);  
            }, false);  
            xhr.addEventListener('load', function (e) {  
                callback('ok', e);  
            }, false);  
            xhr.addEventListener('error', function (e) {  
                callback('error', e);  
            }, false);  
            xhr.addEventListener('abort', function (e) {  
                callback('cancel', e);  
            }, false);  
        }  
        xhr.open('POST', url);  
        xhr.send(fd);  
    };  
 
    //音頻採集  
    recorder.onaudioprocess = function (e) {  
        audioData.input(e.inputBuffer.getChannelData(0));  
        //record(e.inputBuffer.getChannelData(0));  
    };  
 
};  
 
//拋出異常  
HZRecorder.throwError = function (message) {  
    throw new function () { this.toString = function () { return message; };};  
};  
//是否支持錄音  
HZRecorder.canRecording = (navigator.getUserMedia != null);  
//獲取錄音機  
HZRecorder.get = function (callback, config) {  
   if (callback) {
        navigator.mediaDevices
            .getUserMedia({ audio: true })
            .then(function(stream) {
                let rec = new HZRecorder(stream, config);
                callback(rec);
            })
            .catch(function(error) {
                HZRecorder.throwError('無法錄音,請檢查設備狀態');
            });
    }
};  
window.HZRecorder = HZRecorder;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章