تبدیل صدا به متن در JS ( پروژه کاربردی )

تبدیل صدا به متن در JS ( پروژه کاربردی )

صدا به نوشته در JS – در این آموزش یکی مینی پروژه باحال رو هم پیاده سازی می کنیم . ابتدا یاد می گیریم که چطوری با SpeechRecognition کار کنیم .

سپس با api سایت علی بابا پرواز های موجود رو با جزئیات نمایش می دهیم . البته نیاز هست که فیلد هایی را وارد کنیم اما با صوت که به نوشته تبدیل می شود فیلد ها رو پر می کنیم .

دموی برنامه


آشنایی با SpeechRecognition

var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
var recognition = new SpeechRecognition();

option های SpeechRecognition

recognition.continuous = false;
recognition.lang = 'en-US';
recognition.interimResults = false;
recognition.maxAlternatives = 1;
  • continuous : آیا در صورتی که موردی پیدا شد همچنان صوت را دریافت کند یا متوقف شود
  • lang : صوت که دریافت می کند به چه زبانی است برای ایرانی fa-IR
  • interimResults : اینکه نتیجه را مستقیما برگرداند یا کمی بیشتر پردازش کرده و نتیجه نهایی را نمایش دهد
  • maxAlternatives : موارد مرتبط را هم نمایش دهد یا فقط یک نتیجه باشد

دستورات شروع و توقف ضبط صدا

recognition.start();
recognition.stop();


event های SpeechRecognition

onresult زمانی که نتیجه یافت گردید

recognition.onresult = function(event) {
  var result = event.results[0][0].transcript;
}


onspeechend زمانی که دریافت صدا به پایان رسید

recognition.onspeechend = function(event) {}


onnomatch زمانی که هیچ موردی یافت نشد

recognition.onnomatch = function(event) {}


onerror زمانی که خطایی هنگام دریافت صدا رخ داده

recognition.onerror= function(event) {}


شروع پروژه صدا به متن

ابتدا باید کتابخانه های زیر را وارد پروژه خود کنید مثل کد پایین :

  1. skeleton : کتابخانه css
  2. jquery : کتابخانه قدرتمند JS
  3. persian-datepicker : تقویم شمسی برای JS
  4. persian-date : کتابخانه تبدیل تاریخ میلادی به شمسی یا بالعکس

فایل index.html

<!DOCTYPE html>
<html lang="fa">
<head>
    <meta charset="UTF-8">
    <title>Rapidcode.iR - سورس کد</title>
    <link rel="stylesheet" href="static/css/main.css">
	<link rel="stylesheet" href="static/css/lib/normalize.css">
	<link rel="stylesheet" href="static/css/lib/skeleton.css">
	<link rel="stylesheet" href="static/css/lib/persian-datepicker.min.css">
</head>
<body>
    <div class="container">
        <a id="introduce" href="https://rapidcode.ir" target="_blank">رپید کد • کتابخانه مجازی برنامه نویسان</a>

        <form class="booking">
		
		<div class="row">
			<input class="four columns button-primary" type="button" id="getUserVoiceData" value="جستجو بلیت">
		</div>
		
		<div class="row">
		<input class="six columns geobase" data-voice="origin" data-json-key="origin" readonly="readonly" type="text" id="originPlace" placeholder="مبدا">
		<input class="six columns geobase" data-voice="destination" data-json-key="destination" readonly="readonly" type="text" id="destinationPlace" placeholder="مقصد">
		</div>
		
		<div class="row">
		<input class="six columns" data-voice="leavingDate" data-json-key="departureDate" readonly="readonly" type="text" id="leavingDate" placeholder="تاریخ رفت">
		<input class="six columns" data-voice="numberOfPassenger" data-json-key="adult" readonly="readonly" type="text" id="NumberOfPassengers" placeholder="تعداد مسافران">
		</div>
		
		</form>
		
		<div class="row list-content"></div>
       
    </div>


	<script src="static/js/lib/jquery-3.2.1.min.js"></script>
	<script src="static/js/lib/persian-date.min.js"></script>
	<script src="static/js/lib/persian-datepicker.min.js"></script>
    <script src="static/js/app.js"></script>
	<script>
		const datepickerDOM = $("#leavingDate");
		window.dateObject = datepickerDOM.persianDatepicker(
		{
			"inline": false,
			"format": "LLLL",
			"viewMode": "day",
			"initialValue": true,
			"onSelect" : function(){ 
				const currentDateState = date.dateObject.State.gregorian;
			
				window.selectedDate = `${currentDateState.year}-${(currentDateState.month + 1).toString().padStart(2, "0")}-${currentDateState.day.toString().padStart(2, "0")}`;
				
				dateObject.hide();
				getTheVoiceResult(selectedDate);
			}
		});
		
		const date = dateObject.getState().view;
	</script>
</body>
</html>


اسکریپت app.js

  • در این اسکریپت از Audio استفاده کرده ایم جهت پخش صدا برای هر فیلد به طور اختصاصی .
  • از Generator ها استفاده کردیم .
  • با xhr ها جهت ارسال درخواست http با استفاده از مکانیزم AJAX
  • با تابع listDataToHtml داده ها را در قالب html نمایش می دهیم
  • اعداد فارسی را به عامیانه تبدیل کرده با استفاده از regex
  • همچنین از تقویم فارسی جهت دریافت تاریخ رفت استفاده کرده ایم چون با صدا زیاد دقیق به دست نمی آمد .
  • تابع elementFocus جهت نمایش برجسته فیلد فعال است
  • از Web Speech API استفاده کردیم .
const audio = new Audio();
const audioError = new Audio();

var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
const recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.lang = 'fa-IR';
recognition.interimResults = false;
recognition.maxAlternatives = 1;

const voiceList = {
	destination : '/static/audio/destination.mp3',
	leavingDate : '/static/audio/leavingDate.mp3',
	numberOfPassenger : '/static/audio/numberOfPassenger.mp3',
	origin : '/static/audio/origin.mp3',
	notDetected : '/static/audio/notDetected.mp3',
	Problem : '/static/audio/Problem.mp3'
}

const cities = {
	THR:"تهران",
	MHD:"مشهد",
	AWZ:"اهواز",
	SYZ:"شیراز",
	BND:"بندر عباس",
	IFN:"اصفهان",
	TBZ:"تبریز",
	KIH:"کیش"
}

const voiceButtonDOM = document.getElementById('getUserVoiceData');
const inputTextDOM = document.querySelectorAll(".booking input[type='text']");
const listContentDOM = document.getElementsByClassName('list-content')[0];

voiceButtonDOM.onclick = startGettingVoice;

recognition.onresult = function(event) {
 const result = event.results[0][0].transcript;
 getTheVoiceResult(result);
}

recognition.onspeechend = function() {
  recognition.stop();
}

recognition.onnomatch = function(event) {
  playVoiceState("notDetected",audioError);
}

recognition.onerror = function(event) {
  playVoiceState("Problem",audioError);
}

audio.onended = function(){
	if(activeInput.id == 'leavingDate'){
		dateObject.show();
	}else{
		recognition.start();
	}
	
}

// ######## END HANDLER

function playVoiceState(state , audioObj){
	audioObj.src = voiceList[state];
	audioObj.onloadeddata = function(){
		audioObj.play();
	}
}

function * generatorInputList(){
	for(var i=0;i<inputTextDOM.length;i++){
		yield inputTextDOM[i];
	}
}

function tabTheElement(currentElement){
	elementFocus(currentElement);
	const voice = currentElement.dataset.voice;
	playVoiceState(voice, audio);
}

function startGettingVoice(){
	window.inputGenerator = generatorInputList();
	const currentElement = inputGenerator.next().value;
	tabTheElement(currentElement);
}

function elementFocus(customElement){
	
inputTextDOM.forEach(function(element){
	if(customElement == element){
		customElement.classList.add("activeBorder");
		window.activeInput = customElement;
	}
	else if(element.classList.contains("activeBorder"))
		element.classList.remove("activeBorder");
		
});

}

function fixNumbers (str)
{
  var persianNumbers = [/۰/g, /۱/g, /۲/g, /۳/g, /۴/g, /۵/g, /۶/g, /۷/g, /۸/g, /۹/g]
  if(typeof str === 'string')
  {
    for(var i=0; i<10; i++)
    {
      str = str.replace(persianNumbers[i], i);
    }
  }
  return str;
};

function funcReverseString(str) {
    return str.split('').reverse().join('');
}

function seperateNumbers(numberValue){
    if (isNaN(Number(numberValue))) {
        alert("لطفا از وارد کردن حروف خودداری فرمایید !")
        return false;
    }
 
    let seperatedNumber = numberValue.toString();
    seperatedNumber = funcReverseString(seperatedNumber);
    seperatedNumber = seperatedNumber.split("");
 
    let tmpSeperatedNumber = "";
 
    j = 0;
    for (let i = 0; i < seperatedNumber.length; i++) {
        tmpSeperatedNumber += seperatedNumber[i];
        j++;
        if (j == 3) {
            tmpSeperatedNumber += ",";
            j = 0;
        }
    }
 
    seperatedNumber = funcReverseString(tmpSeperatedNumber);
    if(seperatedNumber[0] === ",") seperatedNumber = seperatedNumber.replace("," , "");
    return seperatedNumber;
}

function seperateDateAndTime(date , type="time"){
	const dateList = date.split("T");
	const finallValue = type == "date" ? dateList[0] : dateList[1];
	return finallValue;
}

function getTheVoiceResult(result){
	result = fixNumbers(result);
	if(activeInput.id != "leavingDate")
		activeInput.value = result;
	if(activeInput.classList.contains('geobase')){
		const indexCities = Object.keys(cities);
		indexCities.forEach(function(element){
			const currentCity = cities[element];
			if(currentCity == activeInput.value)
				activeInput.value = `${activeInput.value}-${element}`;
		});
	}
	
	const nextInput = inputGenerator.next().value;
	if(nextInput != undefined)
		tabTheElement(nextInput);
	else{
		const data = {
			child : 0,
			infant : 0,
		}
		
		inputTextDOM.forEach(function(element){
			let finallValue = element.value;
			if(element.classList.contains('geobase')){
				finallValue = element.value.split('-')[1];
			}else if(element.id == "leavingDate"){
				finallValue = selectedDate;
			}if(!isNaN(Number(finallValue))) finallValue = Number(finallValue);
			
			const key = element.dataset.jsonKey;
			data[key] = finallValue;
		});
		
		getOnlineFlight(data);
	}
}

function emptyTheListContent(){
	listContentDOM.innerHTML = "";
}

function listDataToHtml(data){
	data['priceAdult'] = seperateNumbers(data['priceAdult']);
	data['status'] = data['status'] == 0 ? "نا موجود" : data['status'] + " صندلی";
	data['leaveDateTime'] = seperateDateAndTime(data['leaveDateTime']);
	data['arrivalDateTime'] = seperateDateAndTime(data['arrivalDateTime']);
	
	const htmlContent = `<div class="u-full-width content-booking"><img src="${data.airlineLogo}" id="logo"> <h6 id="ariline">${data.airlineName}</h6> <h2 id="title"><span id="from">${data.originName}</span> به <span id="to">${data.destinationName}</span></h2> <h3 id="time"><span id="leave">پرواز : ${data.leaveDateTime}</span>  <span id="arrive">حضور : ${data.arrivalDateTime}</span></h3> <h4 id="price">${data.priceAdult} ریال</h4><h5 id="entity">${data.status}</h5><div></div></div>`;
	
	listContentDOM.innerHTML += htmlContent;
}

function getOnlineFlight(data){
	
	voiceButtonDOM.setAttribute("disabled", "disabled");
	const xhr = new XMLHttpRequest();
	
    const params = JSON.stringify(data);
    console.log(params);
 
    xhr.open("POST", "https://ws.alibaba.ir/api/v1/flights/domestic/available");
	xhr.setRequestHeader('Content-Type', 'application/json');
	
    xhr.onload = function(xhrLoaded){
		
		let data = xhrLoaded.currentTarget.response;
		data = JSON.parse(data);
		
		if(!data.success){
			playVoiceState("notDetected",audioError);
			voiceButtonDOM.removeAttribute("disabled");
			return false;
		}
		
		const RequestID = data.result.requestId;
		
		xhr.open("GET", 'https://ws.alibaba.ir/api/v1/flights/domestic/available/' + RequestID);
		xhr.setRequestHeader('Content-Type', null);
		
		xhr.onload = function(xhr){
			let data = xhr.currentTarget.response;
			data = JSON.parse(data);
			let customData = [];
			emptyTheListContent();
			data.result.departing.forEach(function(val){
				const tempObj = {
					originName : val.originName,
					destinationName : val.destinationName,
					airlineName : val.airlineName,
					leaveDateTime : val.leaveDateTime,
					arrivalDateTime : val.arrivalDateTime,
					priceAdult : val.priceAdult,
					status : val.status,
					airlineLogo : val.airlineLogo
				}
				customData.push(tempObj);
				listDataToHtml(tempObj);
			});
			
			
			voiceButtonDOM.removeAttribute("disabled");
		}
		
		xhr.onerror = function (){
		console.warn("ERROR 2");
		voiceButtonDOM.removeAttribute("disabled");
		};
		
		xhr.send();
		
	}
    xhr.onerror = function (){
		console.warn("ERROR");
		voiceButtonDOM.removeAttribute("disabled");
	};
 
    xhr.send(params);
}


دانلود پروژه صدا به نوشته در JS

توجه داشته باشید که برنامه باید روی سرور یا حداقل یک localhost اجرا شود .

ارسال نظر

جهت استفاده از کد حتما از تگ pre استفاده نمایید .

لیست نظرات

  1. جاوید نورانی
    جاوید نورانی

    درود و روزبخیر میخواستم ببینم اگر بخوایم همین کار رو، به روی یک جستجوی صوتی توی وبسایت پیاده کنیم، امکان پذیره؟ یعنی میخوام یه جستجو صوتی برای سایتم درست کنم که وقتی اسم یکی از محصولاتمو گفتم، همون سرچ بشه و بالا بیاد راهنمایی کنید سپاسگزارم

    04 اسفند 1402 | 17:06:46
  • حسین باقری
    حسین باقری

    درود ، بله امکان پذیر هست اما کار چند خط کد نیست که بخوام راهنمایی تون کنم در صورت نیاز می تونید از این طریق در شبکه های اجتماعی در ارتباط باشید .

    04 اسفند 1402 | 18:13:31
  • amin
    amin

    سلام من در هاست اجرا کردم ولی پروژ کار نکرد؟

    17 بهمن 1401 | 15:24:33
    • حسین باقری
      حسین باقری

      درود ، اگر در تب console از پیغام خطا وجود داره بفرستید تا بررسی بشه .

      17 بهمن 1401 | 22:18:37
  • Mehrdad
    Mehrdad

    آیا از این قابلیت برای ترجمه و درج زیر نویس فیلم میشه استفاده کرد؟

    02 مرداد 1401 | 19:17:23
    • حسین باقری
      حسین باقری

      بله ، اما باید شمرده شمرده و کتابی صحبت کرد .

      05 مرداد 1401 | 14:17:00
    contact us