منبع اصلی نوشتار زیر در این لینک قرار دارد

لگو بازی با گنو/لینوکس:‌ تلفظ کامل یک جمله

سلام

خب تقریباً یک ماه پیش بود که پست «لگو بازی با گنو/لینوکس: تلفظ لغات در NOKIA N900.» رو ارسال کردم و یاد گرفتیم که چطور این کار رو انجام بدیم. ولی خب یه چند تا راه برای گسترش و بهتر کردنش وجود داره و توی این پست می خوایم اینا رو بررسی کنیم.

اوّلین مورد: اگر به یاد داشته باشید در پست قبل گفتم که:

خب تا اینجا هدف ما برآورده شد ولی یه خورده بهتر ترش می کنم. مثلاً اگر تلفظی وجود نداشت می خوام این مسئله رو با من در میون بزاره. برای این کار از دستور if استفاده می کنم تا به طور مستقیم وجود فایل رو بررسی کنه اگر وجود داشت پخش بشه و اگر وجود نداشت یه متن ساده که حاکی از وجود نداشتن فایل تلفظ برای کلمه ی مورد نظر هست رو نشون بده.

خب حالا نظرم عوض شده (اگر اشتباه نکنم این مورد رو شاهین توی یه گفتگوی اینترنتی برام یادآوری کرد. ممنون ازش :)‌ ) و می خوام اگر فایل تلفظی برای کلمه ای وجود نداشت espeak اون رو تلفظ کنه پس فقط کافیه قسمت else دستور if رو تغییر بدم و به جای چاپ متن دستور espeak رو با ورودی همون کلمه (آرگومان) بنویسم. پس فقط همون خط رو عوض  می کنم و اسکریپت به این شکل میشه:

#!/bin/bash
declare DIR
DIR=`echo $2 | cut -c1`
if test -f /home/user/MyDocs/others/.WyabdcRealPeopleTTS/$DIR/$2.wav; then
for ((count=1; count<=$1; count++)); do
printf "%s$count "
aplay /home/user/MyDocs/others/.WyabdcRealPeopleTTS/$DIR/$2.wav 2> /dev/null
sleep 1
done
printf ".\\n"
else
espeak ${args[$i]} &> /dev/null
fi
exit 0

خب تا این  جا به جز یه مورد همه چی خوبه, اون یه مورد هم این هست که من چرا آخر دستور نوشتم

&> /dev/null

دستور espeak هنگام کار خروجی هایی داره (error/output)  که برای ما لازم نیستن و فقط ترمینال رو شلوغ می کنن و من با اضافه کردن این دستور خواستم همه ی standard streams رو یک جا به فایل

/dev/null

 ارسال کردم. برای اینکه با standard streams آشنا بشین می تونید به اینجا و اینجا مراجعه کنید.

‏‫‫‫‫دوّمین مورد:این مورد هم با دیدن نظر شاهین در پست قبل (که در بالا لینک دادم) به ذهنم رسید. (یعنی اوّلش فکر کردم منظورش این هست ولی بعداً باهاش صحبت کردم و دیدم این نبود:D ) یعنی بتونیم کل یه جمله رو تلفظ کنیم. کار سختی نیست ما که هر کلمه ای رو تونستیم با آخرین آپدیت (اسکریپت بالا) تلفظ کنیم هر جمله هم از تعدادی کلمه تشکیل شده که بینشون یک فاصله قرار گرفته پس فقط کافیه کلمات یک جمله رو هم یکی یکی و پشت سر هم تلفظ کنیم. تنها مشکل سر راهمون این هست که بتونیم کلمات یک جمله رو بدست بیاریم، لابد الان تو فکرتون هست که یه برنامه بنویسم و که همه ی کاراکترهای ورودی رو بررسی کنه تا اگر فاصله (space) بود اون رو به سبب بک کلمه بودن جدا کنه!! من می گم این کار لازم نیست خوبی شل اسکریپت هم اینه که اگر کل جمله رو از ورودی به اسکریپت بدیم هر کلمه یک آرگومان حساب میشه (البته دلیل اصلیش IFS هست!!) یعنی جدا از بقیه. و خب من می تونم تمامی آرگومان ها رو توی یک آرایه ذخیره کنم و یکی یکی تلفظشون کنم. (البته من همین اسکریپت رو تغییر ‫‏میدم.) برای ایجاد آرایه مثل تعریف متغیرها عمل می کنیم با این تفاوت که آپشن a رو به دستور declare اضافه می کنیم تا متوجه بشه که این متغیر یه آرایه هست.

declare -a args;

 خب میدونیم که عبارت

$@

 به همه ی آرگومان ها اشاره می کنه پس من اگر آرایه ای رو که تعریف کردم به شکل زیر مقدار دهی کنم هر آرگومان داخل یکی از خانه های آرایه قرار می گیره (و دنبال کردن خانه های آرایه به راحتی با حلقه امکان پذیره):

args=("$@");

گفتیم حلقه! می خوام از حلقه ی for استفاده کنم که شمارندش از ۱ شروع می شه و تا… و تا … راستی حلقه تا کجا باید ادامه پیدا کنه؟ خب می دونیم که تعداد کلمات جمله ثابت نیست و این رو می دونیم که همه ی کلمات داخل آرایه قرار گرفتن پس میشه طول آرایه رو بررسی کرد یا مثلاً چون گفتنم هر کلمه یک آرگومان حساب شده پس تعداد آرگومانها همون تعداد کلمات خواهد بود. آره این راححتره. تعداد آرگومان رو همیشه می شه از طریق مقدار

$#

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

for (( i=0; i < $#; i++)); do
### commands
done

خب چون گفته بودیم می خوایم هر کلمه از جمله رو جدا گانه تلفظ کنیم کافیه به جای commands داخل حلقه قسمت تلفظ کننده ی اسکریپت بالا رو اضافه کنیم. در نهایت اسکریپت چیزی شبیه به این میشه (البته مستقل از قبلی ):

#!/bin/bash
declare -i i;
declare -a args;
declare DIR;

args=("$@");
for (( i=0; i < $#; i++)); do
    DIR=`echo ${args[$i]} | cut -c1`;
    if test -f /home/wahid/WyabdcRealPeopleTTS/$DIR/${args[$i]}.wav; then
	aplay /home/wahid/WyabdcRealPeopleTTS/$DIR/${args[$i]}.wav 2> /dev/null
    else
	espeak ${args[$i]} &> /dev/null;
    fi
done
exit 0

اجراش می کنیم و به به . موقع امتحان جملات و لذت بردن از دستاوردمون Oooops! یه مشکل! وقتی کلمه ای رو با یک با بیشتر حرف بزرگ تایپ می کنیم کلمه از طریق espeak تلفظ می شه! مشکل اینجاست که یادمون رفته بود که گنو/لینوکس case sensitive هست. یعنی اینکه به حروف کوچک و بزرگ حساس هست یعنی براش hello با Hello و … فرق داره! یعنی باید کل جمله رو با حروف کوچک بنویسیم ولی خیلی بده کاربر رو محدود کنیم و بهش بگین این کارو بکن یا نکن ناسلامتی داریم از آزادی حرف می زنیم!! پس باید بزاریم کاربر جلو ترمینال وقتی داری با اسکریپت کار می کنه جولان بده و ما خودمون ورودی رو به شکل درست تبدیل کنیم. پس یه صورت مسئله ی جدید می نویسیم: «چطوری ورودی رو به حروف کوچک تبدیل کنیم؟» جوابش سخت نیست، یه دستوری داریم به اسم tr که می تونه کار مورد نظر ما رو انجام بده. (یه نگاهی به man اش بندازین!)  شکلش به این صورت هست:

tr  \'[:upper:]\' \'[:lower:]\'

یعنی همه ی کاراکتر های حروف بزرگ ورودیت رو به حرف کوچک تبدیل کن. طبیعی هم هست که با  حروف کوچک کاری نخواهد داشت. خب ورودی ما هم همون کلمات هست پس من خط ششم رو به شکل زیر تغییر میدم:

args=(`echo $@ | tr \'[:upper:]\' \'[:lower:]\'`);

فکر میکنم به اندازه ی کافی گویا باشه تا نیازی به توضیح نداشته باشه.

پس اسکریپت در نهایت به شکل زیر میشه:

#!/bin/bash
declare -i i;
declare -a args;
declare DIR;
args=(`echo $@ | tr \'[:upper:]\' \'[:lower:]\'`);
for (( i=0; i < $#; i++)); do
    DIR=`echo ${args[$i]} | cut -c1`;
    if test -f /home/wahid/WyabdcRealPeopleTTS/$DIR/${args[$i]}.wav; then
    aplay /home/wahid/WyabdcRealPeopleTTS/$DIR/${args[$i]}.wav 2> /dev/null
    else
    espeak ${args[$i]} &> /dev/null;
    fi
done
exit 0

پی نوشت: از لحاظ فنی شاید نتیجه این اسکریپت خیلی بی روح باشه ولی هدف فقط انجام یه تصمیم و یادگیری چیزهای جالب و جدید در این راه بود.



برچسب ها : , , , , , , , ,

به سیاره لینوکس امتیاز دهید

به اين صفحه امتياز دهيد