تبدیل صدا به متن در 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. 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