ধরা যাক আপনার অ্যাপের মাধ্যমে ইউজাররা বিভিন্ন আর্টিকেল পড়তে পারেন। পড়ার পাশাপাশি আপনি যদি আর্টিকেলগুলো শোনানোর ব্যবস্থা করতে চান তাহলে কী করবেন? একটা কাজ করা যায়, আর্টিকেলগুলো রিডিং পড়ে রেকর্ড করে mp3 ফাইলগুলো অ্যাপে দিয়ে দেয়া বা সার্ভার থেকে পাঠানো। কিন্তু কাজটা খুব একটা সহজ নয়। এই ভয়ংকর বোরিং কাজগুলোর বদলে ১৫-২০ লাইনের কোডের মাধ্যমেই দারুণ এই ফিচারটি implement করা যায়। কয়েক দিন আগে আমাদের টিমের ডেভেলপ করা Editorial Word অ্যাপের একজন ইউজার মেইল করেছিলেন। এই অ্যাপে ইংরেজি দৈনিক পত্রিকার এডিটোরিয়ালগুলো পড়া যায়। ঐ ইউজার রিকোয়েস্ট করেছিলেন আর্টিকেল শোনানোর ব্যবস্থা করার জন্য। ২ দিন পর আপডেটে এই ফিচার অ্যাড করে দিয়েছিলাম। আপনিও চাইলে আপনার অ্যাপকেও কথা বলাতে পারেন।
TextToSpeech Class in Android
TTS Engine (Text to speech engine) ব্যবহার করে TextToSpeech ক্লাসের মাধ্যমে যে কোনো টেক্সটকে voice speech এ কনভার্ট করা যায়। অল্প কয়েক লাইনের কোড লিখেই এটি করে ফেলা যায়। কিন্তু সমস্যা হচ্ছে আপনার টেক্সটের সাইজ যদি চার হাজার অক্ষরের বেশি হয় সে ক্ষেত্রে TextToSpeech ক্লাস কাজ করবে না। উপরে উল্লেখিত অ্যাপে আমরা বড় বড় আর্টিকেল নিয়ে কাজ করেছি। কোনো কোনো আর্টিকেলে ৫-৬ হাজার character-ও থাকে। তাই পুরো আর্টিকেলটাকে ভাগ ভাগ করে একটার পর একটা টেক্সটকে রিড করানো হয়েছে। এর চেয়ে ভাল বুদ্ধি আর পাওয়া যায় নাই।
আপনার টেক্সটকে কিভাবে ভাংবেন সেটা অনেকাংশেই আপনার প্রোজেক্টের ধরনের উপর নির্ভর করে। আমাদের প্রোজেক্টের ক্ষেত্রে অনেকগুলো প্যারাগ্রাফের সমন্বয়ে একটা আর্টিকেল ছিল। ধরুন, পুরো আর্টিকেলটা একটা String object এর মধ্যে রাখা আছে। প্রতিটা প্যারাগ্রাফকে নিজেদের মধ্যে আলাদা করা হয়েছে “\n\n” দ্বারা। তাতে এই টেক্সটি একটা TextView এর ভিতর .setText() করলে সুন্দর প্যারাগ্রাফ আকারে আর্টিকেলটা শো করে। যেহেতু আমার এই ইউজ কেসের ক্ষেত্রে আমি জানি যে প্রতিটা আর্টিকেলের শেষে দুইটা new line দেয়া আছে আর প্যারাগ্রাফগুলো কখনোই ৪০০০ অক্ষরের বেশি হবে না তাই আমার এক্ষেত্রে লজিক হচ্ছে আমার পুরো আর্টিকেলটা (String object) split করবো “\n\n” এর ভিত্তিতে। অর্থাৎ যেখানেই “\n\n” পাওয়া যাবে সেখানেই স্টিংকে টুকরা করে একটা স্ট্রংয়ের অ্যারেতে রেখে দিব।
Regex এর মাধ্যমে খুব সহজে এই কাজটা করা যায়। Split করার কোডটুকু নিচে দেয়া হলোঃ
private String text = "This is paragraph 1\n\nThis is paragraph 2\n\n"; private String[] paragraphList; paragraphList = text.split("\\n\\n"); //split by Regular Expression
আপনার মূল টেক্সটের প্রতিটা প্যারাগ্রাফের পরে যদি আপনি ‘$’ সাইন দিয়ে রাখেন তাহলে regex এ “$” দিবেন। অথবা আপনি যদি চান অক্ষরের length দিয়ে split করতে সেটাও সম্ভব। যেমন ১০০০ অক্ষরের একেকটা স্ট্রিং আলাদা আলাদা করবেন। স্ট্রিংয়ের অ্যারের প্রথম ইন্ডেক্সে থাকবে টেক্সটের প্রথম ১০০০ অক্ষর, দ্বিতীয় ইনডেক্সে থাকবে টেক্সটের দ্বিতীয় ১০০০ অক্ষর।
TextToSpeech class এর initialization আর কিছু event/progress listener দেখানো হলো নিচের কোডে।
textToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() { @Override public void onInit(int status) { if(status == TextToSpeech.SUCCESS){ textToSpeech.setSpeechRate((float) 1.25); //1.0 is normal. lower value decrease the speed and upper value increase textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() { @Override public void onStart(String s) { Logger.d("Started TextToSpeech"); } @Override public void onDone(String s) { paragraphCount++; Logger.d("Done text chunk: " + paragraphCount + " List Length: " + paragraphListLength); if(paragraphCount==paragraphListLength){ paragraphCount = 0; runOnUiThread(new Runnable() { @Override public void run() { button.setText("Play"); } }); } } @Override public void onError(String s) { Logger.d("Error occurred: " + s); } }); } } });
onInit() মেথডের ভিতর speech এর speed rate, pitch rate ইত্যাদি সেট করে দিতে পারবেন। সকল properties-গুলোর ব্যাপারে আরো বিস্তারিত জানার জন্য অফিসিয়াল ডকুমেন্টেশন দেখতে পারেন।
আমার এই প্রোজেক্টের UI তে একটা বাটন আর ScrollView এর ভিতর একটা TextView আছে। বাটনে “PLAY” লেখা থাকবে। ক্লিক করার পর লেখা থাকবে “STOP”. Speech শেষ হয়ে গেলে বাটনের টেক্সট পরিবর্তন হয়ে set হবে “PLAY”.
TextToSpeech এর onDone() state এর ভেতরে সরাসরি UI-তে কোনো আপডেট করা যায় না। এজন্য ব্যবহার করতে হয় runOnUiThread(). আপনি যদি এটা Fragment এ ব্যবহার করেন তাহলে লিখতে হবে getActivity().runOnUiThread(). এই thread এর run() মেথডের মধ্যে আপনার UI সংক্রান্ত যাবতীয় কাজগুলো করতে পারবেন। Progress event এর অন্যান্য state গুলোর ভিতরে Pretty Logger Library এর মেথড ব্যবহার করে লগ মেসেজ প্রিন্ট করা হয়েছে।
এ তো গেল ইনিশিয়ালাইজেশনের পার্ট। এখন দেখব বাটন ক্লিকে কিবাবে play/stop করা যায়ঃ
@Override public void onClick(View view) { if(!textToSpeech.isSpeaking()) { button.setText("Stop"); HashMap<String, String> map = new HashMap<>(); map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "speak"); for(int i=0; i<paragraphList.length; i++) { textToSpeech.speak(paragraphList[i], TextToSpeech.QUEUE_ADD, map); textToSpeech.playSilence(250, TextToSpeech.QUEUE_ADD, null); } } else { textToSpeech.stop(); button.setText("Play"); } }
উপরের কোডে লুপের ভিতরে speak() ও playSilence() মেথড কল করা হয়েছে। speak() এর তৃতীয় প্যারামিটারে পাঠানো হয়েছে একটা HashMap এর object. ম্যাপের ভ্যালু সেট করা হয়েছে “speak”. এখানে আপনি আপনার ইচ্ছা মত যে কোনো একটা আইডি দিতে পারেন। এই আইডির মাধ্যমেই পরবর্তীতে textToSpeech অবজেক্টের বিভিন্ন লিসেনার কল হবে। এখানে এই ম্যাপটা প্যারামিটার হিসাবে পাস না করলে progress listener এর onStart(), onDone() ইত্যাদি মেথড কল হবে না।
আর playSilence() এর কাজ হচ্ছে প্রতিটা প্যারাগ্রাফ পড়ার পর একটু থামা। কতক্ষণ থামতে হবে সেটা মিলিসেকেন্ড এককে প্রথম প্যারামিটারে পাঠানো হয়েছে। আপনার টেক্সট যদি ৪০০০ অক্ষরের কম হয় সেক্ষেত্রে এই লুপ-টুপ ঘুরানো লাগবে না। speak() এর প্রথম প্যারামিটারে string object টা পাঠায় দিবেন। সুন্দর মত কাজ করবে।
এই পোস্টে একদম ব্যাসিক ফাংশনালিটিগুলো দেখানো হয়েছে। আরো বিস্তারিত জানার জন্য গুগল তো রয়েছেই!
পুরো প্রোজেক্টের সোর্স কোড পাওয়া যাবে আমার গিটহাব রিপোজিটরিতে।
I want to add one feature where user can set how many times to text will speak .If user set 3 then after speaking text 3 times then will be stop .So how can do it?
Try this. It should work.
বাংলা লেখা শোনা যাবে এই মেথডএ
বাংলায় TTS সাপোর্ট এখনো নাই
ভাইয়া আপনার কমেন্টটা ১বছর আগের। বাট এখন নিশ্চয় সম্ভব। বাংলায় লেখা কথা শোনা… প্লিজ হেল্প মি
সার্চ করে বাংলার জন্য পেলাম না কিছু। আপনি কিছু পেলে জানাবেন।
Vai ami jodi input hisebe pdf or doc file dite chai tahole ki korte hobe?!
Is Now Bangla supported for TTS? I’ve tried google. Haven’t found anything promising.
As far I know, it’s not supported yet.
Is there any way I can detect each word which is being currently spoken by TTS currently to highlight it?