ในตอนที่ผ่านมา เราได้เริ่มต้น แนวทางการประยุกต์
ใช้งาน Web Speech API เพื่อสร้างเป็น jQuery Plugin สำหรับ
ใช้งานร่วมกับ Bootstrap ไปเบื้องต้นแล้ว เนื้อหาตอนต่อไปนี้
เราจะมาต่อกันในส่วนของ การปรับใช้งาน Web Speech API มากำหนด
ใช้งานใน plugin ทบทวนตอนที่แล้วได้ที่
ประยุกต์สร้าง jQuery Web Speech ร่วมกับ Bootstrap ตอนที่ 1 http://niik.in/970
https://www.ninenik.com/content.php?arti_id=970 via @ninenik
สำหรับรูปแบบการใช้งาน Web Speech API เราจะใช้โค้ดจากเนื้อหาบทความ
การใช้งาน web speech api แปลงเสียง เป็นข้อความ ใน html5 http://niik.in/558
https://www.ninenik.com/content.php?arti_id=558 via @ninenik
ปรับใช้งาน Web Speech API
สามารถ เข้าไปดู DEMO และรูปแบบการทำงานที่หน้าบทความดังกล่าวได้
ในที่นี้จะยกโค้ดทั้งหมดมาตามด้านล่าง
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | var final_transcript = '' ; // ตัวแปร สำหรับเก็บข้อความที่แปลงจากเสียง var recognizing = false ; // กำหนดค่าเริ่มต้นการจดจำเสียง เริ่มต้น ให้เป็น false ไม่ทำงาน var language = 'th-TH' ; // กำหนดภาษา th-TH, $( function (){ // ตรวจสอบ browser ว่าสนับสนุนการใช้งาน Speech API หรือไม่ if (!( 'webkitSpeechRecognition' in window)) { alert( "Your Browser does not support the Speech API" ); } else { // สร้าง recognition object และกำหนด event handlers // (onstart , onerror, onend, onresult) var recognition = new webkitSpeechRecognition(); // สร้าง recognition object recognition.continuous = true ; // กำหนด true ให้รับค่า จากเสียงไปเรื่อยๆ จนกว่าจะกดปุ่มหยุด recognition.interimResults = true ; // แสดงข้อความช่วงจังหวะหรือไม่ กรณีพูดยาวๆ recognition.lang = language; // กำหนดภาษา จากตัวแปรด้านบน recognition.onstart = function () { // เมื่อเกิดการเริ่มทำงานของการจดจำเสียง มาจากคำสั่ง recognition.start(); recognizing = true ; // เปลี่ยนค่าให้เริ่มทำการจดสับเสียงเป็น true เริ่มทำงาน $( '#instructions' ).html( 'Speak slowly and clearly' ); // แสดงคำแนะนำ $( '#start_button' ).html( 'Click to Stop' ); // เมื่อกดแล้วเปลี่ยนข้อความปุ่มเป็น คลิกอีกทีเพื่อหยุด หรือ Stop }; recognition.onerror = function (event) { // ถ้าเกิดข้อผิดพลาด ทำงานส่วนนี้ $( '#instructions' ).html( "There was a recognition error..." ); // แจ้งสถานะถ้าเกิดข้อผิดพลาด }; recognition.onend = function () { // ถ้าจบการทำงาน เช่นหยุดด้วยคำสั่ง recognition.stop(); // หรือไม่ได้พูดเพื่อใช้งาน การจดจำเสียงนาน ก็จะหยุดการทำงานเอง recognizing = false ; // กำหนดให้การจดจำเสียงอยูในสถานะหยุดการทำงาน $( '#instructions' ).html( 'Done' ); // แสดงสถานะว่าเสร็จสิ้นแล้ว Done $( '#start_button' ).html( 'Click to Start' ); // เปลี่ยนข้อความปุ่มกดให้เป็นค่าเริ่มต้น }; recognition.onresult = function (event) { // เมื่อแปลงเสียงเป็นข้อความสำเร็จ ส่งผลลัธ์กลับมา // ตัวแปรไว้เก็บข้อความในช่วงจังหวะหนึ่งจังหวะใดบางช่วง กรณีพูดยาวๆ var interim_transcript = '' ; // ปกติค่านี้ไม่ค่อยได้ใช้ จะใช้ค่า final มากกว่า // ถอดจากข้อความจาก array ผลลัพธ์ for ( var i = event.resultIndex; i < event.results.length; ++i) { // ถ้าเป็นค่าสุดท้ายแล้ว หยุดพูด หรือไม่ได้พูดต่อ if (event.results[i].isFinal) { // เอาข้อความผลัพธ์ที่ได้ มาต่อๆ กันและกับในตัวแปร final_transcript final_transcript += event.results[i][0].transcript+ ' ' ; } else { // ถ้าเป็นค่าข้อความระหว่างช่วงเวลา ในกรณีพูดยาวๆ เก็บในตัวแปร // เก็บในตัวแปร interim_transcript interim_transcript += event.results[i][0].transcript+ ' ' ; } } // บรรทัดที่ เอาไว้ทดสอบดูค่า ใน console ไม่ได้ใช้ปิดไป // console.log("interim: " + interim_transcript); // console.log("final: " + final_transcript); if (final_transcript.length > 0) { // นับความยาวข้อความ ถ้ามากกว่า 0 แสดงว่ามีค่า // ตัวแปร final_transcript คือค่าข้อความที่ได้ เอาไปใช้งานต่อได้ $( '#transcript' ).val(final_transcript); // แสดงค่าใน textarea } }; // ภ้ากดปุ่ม id start_button $( "#start_button" ).click( function (e) { e.preventDefault(); // การจดจำเสียงกำลังทำงานอยู่หรือไม่ กดครั้งแรก จะยังไม่ทำงาน if (recognizing) { // ภ้าทำงานอยู่ เมื่อกดก็จะเป็นหยุด recognition.stop(); // ให้หยุดการจัดจำเสียง $( '#start_button' ).html( 'Click to Start Again' ); // เปลี่ยนข้อความปุ่ม แนะนำกดใหม่ ถ้าต้องการจดจำเสียงอีกครั้ง recognizing = false ; // เปลี่ยนสถานะว่าหยุดทำงาน } else { // ถ้ากดแล้วสถานะการจดจำเสียงหยุดอยู่ ให้ทำงาน final_transcript = '' ; // กำหนดตัวแปรเก็บข้อความเป็นค่าว่างก่อน // ขออนุญาตใช้งานการจดจำเสียงและเริ่มใช้งาน recognition.start(); // แจ้งคำแนะนำว่าให้ กด allow หรือตกลง เพื่ออนุญาตให้ใช้งาน Microphone $( '#instructions' ).html( 'Allow the browser to use your Microphone' ); $( '#start_button' ).html( 'waiting' ); // เปลี่ยนข้อความปุ่ม ว่ารอ waiting $( '#transcript' ).val( '' ); // แสดงข้อความเป็นค่าว่าง } }); } }); |
ในโค้ดข้างต้น เราจะเห็นความสัมพันธ์ของตัวแปรต่างๆ พอแยกอธิบายได้ดังนี้
ตัวแปร 3 ตัวแรก เป็นส่วนของ option การกำหนด่าเริ่มต้น
1 2 3 4 | // บรรทัดที่ 1,2,3 var final_transcript = '' ; // ตัวแปร สำหรับเก็บข้อความที่แปลงจากเสียง var recognizing = false; // กำหนดค่าเริ่มต้นการจดจำเสียง เริ่มต้น ให้เป็น false ไม่ทำงาน var language = 'th-TH' ; // กำหนดภาษา th-TH, |
และส่วนการทำงาน จะมี element ที่เกี่ยวข้องอยู่ด้วยกัน 3 ตัวคือ
1 2 3 4 | // บรรทัดที่ใช้งาน 22,23,28,35,36,63,69,75,84,85,86 $( '#instructions' ) // ส่วนอ้างอิงสำหรับแสดงข้อความแจ้ง คำแนะนำ $( '#start_button' ) // ปุ่มสำหรับเพิ่มหรือหยุดการพิมพ์ด้วยเสียง $( '#transcript' ) // ส่วนที่แสดงผลลัพธ์ข้อความ ที่ได้จากข้อความเสียง |
จะเห็นว่าแบบเดิม เรากำหนดการเรียกใช้งานแบบอ้างอิงผ่าน id แบบตายตัว ถ้าจะใช้งาน ก็ต้องมาเปลี่ยนค่า
ของส่วนนี้เป็นค่าที่ต้องการ แต่ถ้าเราสร้างเป็น plugin ก็จะไม่ต้องมากำหนดในส่วนนี้ แต่จะอ้างอิงความสัมพันธ์
และเรียกใช้งานตามรูปแบบให้อัตโนมัติ
เราจะดึงโค้ดทั้งหมดมาไว้ใน plugin ที่เราสร้างในตอนที่แล้ว โดยจะ highlight ส่วนของโค้ดที่ยกมา
เพื่อแยกให้เห็นแต่ละส่วนชัดเจนขึ้น
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | <script type= "text/javascript" > // JavaScript Document ( function ($){ var final_transcript = '' ; // ตัวแปร สำหรับเก็บข้อความที่แปลงจากเสียง var recognizing = false; // กำหนดค่าเริ่มต้นการจดจำเสียง เริ่มต้น ให้เป็น false ไม่ทำงาน var language = 'th-TH' ; // กำหนดภาษา th-TH, // type4me คือชื่อของ plugin ที่เราต้องการ $.fn.type4me = function ( options ) { // กำหนดให้ plugin ของเราสามารถ รับค่าเพิ่มเติมได้ มี options // ส่วนนี้ สำหรับกำหนดค่าเริ่มต้น var defaults={ btnSpeakObj:[], btnResetObj:[], }; // ส่วนสำหรับ เป็นต้วแปร รับค่า options หากมี หรือใช้ค่าเริ่มต้น ถ้ากำหนด var settings = $.extend( {}, defaults, options ); /// คืนค่ากลับ การทำงานของ plugin return this.each( function (k, ele) { // กำหนดใช้งาน key หรือ index // ตรวจสอบ browser ว่าสนับสนุนการใช้งาน Speech API หรือไม่ if (!( 'webkitSpeechRecognition' in window)) { alert( "Your Browser does not support the Speech API" ); } else { // สร้าง recognition object และกำหนด event handlers // (onstart , onerror, onend, onresult) var recognition = new webkitSpeechRecognition(); // สร้าง recognition object recognition.continuous = true; // กำหนด true ให้รับค่า จากเสียงไปเรื่อยๆ จนกว่าจะกดปุ่มหยุด recognition.interimResults = true; // แสดงข้อความช่วงจังหวะหรือไม่ กรณีพูดยาวๆ recognition.lang = language; // กำหนดภาษา จากตัวแปรด้านบน recognition.onstart = function () { // เมื่อเกิดการเริ่มทำงานของการจดจำเสียง มาจากคำสั่ง recognition.start(); recognizing = true; // เปลี่ยนค่าให้เริ่มทำการจดสับเสียงเป็น true เริ่มทำงาน $( '#instructions' ).html( 'Speak slowly and clearly' ); // แสดงคำแนะนำ $( '#start_button' ).html( 'Click to Stop' ); // เมื่อกดแล้วเปลี่ยนข้อความปุ่มเป็น คลิกอีกทีเพื่อหยุด หรือ Stop }; recognition.onerror = function (event) { // ถ้าเกิดข้อผิดพลาด ทำงานส่วนนี้ $( '#instructions' ).html( "There was a recognition error..." ); // แจ้งสถานะถ้าเกิดข้อผิดพลาด }; recognition.onend = function () { // ถ้าจบการทำงาน เช่นหยุดด้วยคำสั่ง recognition.stop(); // หรือไม่ได้พูดเพื่อใช้งาน การจดจำเสียงนาน ก็จะหยุดการทำงานเอง recognizing = false; // กำหนดให้การจดจำเสียงอยูในสถานะหยุดการทำงาน $( '#instructions' ).html( 'Done' ); // แสดงสถานะว่าเสร็จสิ้นแล้ว Done $( '#start_button' ).html( 'Click to Start' ); // เปลี่ยนข้อความปุ่มกดให้เป็นค่าเริ่มต้น }; recognition.onresult = function (event) { // เมื่อแปลงเสียงเป็นข้อความสำเร็จ ส่งผลลัธ์กลับมา // ตัวแปรไว้เก็บข้อความในช่วงจังหวะหนึ่งจังหวะใดบางช่วง กรณีพูดยาวๆ var interim_transcript = '' ; // ปกติค่านี้ไม่ค่อยได้ใช้ จะใช้ค่า final มากกว่า // ถอดจากข้อความจาก array ผลลัพธ์ for ( var i = event.resultIndex; i < event.results.length; ++i) { // ถ้าเป็นค่าสุดท้ายแล้ว หยุดพูด หรือไม่ได้พูดต่อ if (event.results[i].isFinal) { // เอาข้อความผลัพธ์ที่ได้ มาต่อๆ กันและกับในตัวแปร final_transcript final_transcript += event.results[i][0].transcript+ ' ' ; } else { // ถ้าเป็นค่าข้อความระหว่างช่วงเวลา ในกรณีพูดยาวๆ เก็บในตัวแปร // เก็บในตัวแปร interim_transcript interim_transcript += event.results[i][0].transcript+ ' ' ; } } // บรรทัดที่ เอาไว้ทดสอบดูค่า ใน console ไม่ได้ใช้ปิดไป // console.log("interim: " + interim_transcript); // console.log("final: " + final_transcript); if (final_transcript.length > 0) { // นับความยาวข้อความ ถ้ามากกว่า 0 แสดงว่ามีค่า // ตัวแปร final_transcript คือค่าข้อความที่ได้ เอาไปใช้งานต่อได้ $( '#transcript' ).val(final_transcript); // แสดงค่าใน textarea } }; // ภ้ากดปุ่ม id start_button $( "#start_button" ).click( function (e) { e.preventDefault(); // การจดจำเสียงกำลังทำงานอยู่หรือไม่ กดครั้งแรก จะยังไม่ทำงาน if (recognizing) { // ภ้าทำงานอยู่ เมื่อกดก็จะเป็นหยุด recognition.stop(); // ให้หยุดการจัดจำเสียง $( '#start_button' ).html( 'Click to Start Again' ); // เปลี่ยนข้อความปุ่ม แนะนำกดใหม่ ถ้าต้องการจดจำเสียงอีกครั้ง recognizing = false; // เปลี่ยนสถานะว่าหยุดทำงาน } else { // ถ้ากดแล้วสถานะการจดจำเสียงหยุดอยู่ ให้ทำงาน final_transcript = '' ; // กำหนดตัวแปรเก็บข้อความเป็นค่าว่างก่อน // ขออนุญาตใช้งานการจดจำเสียงและเริ่มใช้งาน recognition.start(); // แจ้งคำแนะนำว่าให้ กด allow หรือตกลง เพื่ออนุญาตให้ใช้งาน Microphone $( '#instructions' ).html( 'Allow the browser to use your Microphone' ); $( '#start_button' ).html( 'waiting' ); // เปลี่ยนข้อความปุ่ม ว่ารอ waiting $( '#transcript' ).val( '' ); // แสดงข้อความเป็นค่าว่าง } }); } // โค้ตสำหรับ การทำงานของ plugin settings.btnResetObj[k] = $(` <div class = "input-group-prepend" style= "cursor: pointer" > <span class = "input-group-text" ><i class = "far fa-times-circle" ></i></span> </div> `); settings.btnSpeakObj[k] = $(` <div class = "input-group-append" style= "cursor: pointer" > <span class = "input-group-text" ><i class = "fas fa-microphone" ></i></span> </div> `); // ใช้ back qoute กดปุ่ม alt + 96 $(this) .wrap( '<div class="input-group">' ) .before(settings.btnResetObj[k]) .after(settings.btnSpeakObj[k]); // กำหนดการทำงานปุ่ม เริ่มพิมพ์ด้วยเสียง settings.btnSpeakObj[k].click( function (e) { e.preventDefault(); console.log( 'Microphone click' ); }); // ภ้ากดปุ่ม reset ข้อความ settings.btnResetObj[k].click( function (e) { e.preventDefault(); console.log( 'reset click' ); }); }); }; })(jQuery); $( function (){ $( ".use-voice" ).type4me(); }); </script> |
ต่อไปเป็นส่วนของการจัดการตัวแปร และการจัดการโค้ด ส่วนนี้ จะไม่อธิบายทุกบรรทัด ให้เปรียบเทียบโค้ดเดิม
กับโค้ดที่ปรับแต่งแล้วด้านล่าง
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | <script type= "text/javascript" > // JavaScript Document ( function ($){ // type4me คือชื่อของ plugin ที่เราต้องการ $.fn.type4me = function ( options ) { // กำหนดให้ plugin ของเราสามารถ รับค่าเพิ่มเติมได้ มี options // ส่วนนี้ สำหรับกำหนดค่าเริ่มต้น var defaults={ recognizing:false, // กำหนดค่าเริ่มต้นการจดจำเสียง เริ่มต้น ให้เป็น false ไม่ทำงาน language: 'th-TH' , // กำหนดภาษา th-TH, btnSpeakObj:[], // สำหรับ object ปุ่มเริ่มพิมพ์ด้วยเสียง btnResetObj:[], // สำหรับ object ปุ่มรีเซ็ตข้อความ transcript:[], // สำหรับ object ที่เป็น input หรือ textarea ที่จะแสดงข้อความ labelInstruction:[], // สำหรับ object แสดงคำแนะนำ คำเตือน final_transcript:[], // ตัวแปร สำหรับเก็บข้อความที่แปลงจากเสียง }; // ส่วนสำหรับ เป็นต้วแปร รับค่า options หากมี หรือใช้ค่าเริ่มต้น ถ้ากำหนด var settings = $.extend( {}, defaults, options ); /// คืนค่ากลับ การทำงานของ plugin return this.each( function (k, ele) { // กำหนดใช้งาน key หรือ index // ตรวจสอบ browser ว่าสนับสนุนการใช้งาน Speech API หรือไม่ if (!( 'webkitSpeechRecognition' in window)) { alert( "Your Browser does not support the Speech API" ); return false; // หากไม่สามารถใช้งานได้ ให้ออกจากลูป } else { // โค้ตสำหรับ การทำงานของ plugin settings.btnResetObj[k] = $(` <div class = "input-group-prepend" style= "cursor: pointer" > <span class = "input-group-text" ><i class = "far fa-times-circle" ></i></span> </div> `); settings.btnSpeakObj[k] = $(` <div class = "input-group-append" style= "cursor: pointer" > <span class = "input-group-text" ><i class = "fas fa-microphone" ></i></span> </div> `); settings.labelInstruction[k] = $( '<label class="instructions"></label>' ); settings.transcript[k] = $(this); settings.labelInstruction[k].hide(); // ใช้ back qoute กดปุ่ม alt + 96 $(this) .after(settings.labelInstruction[k]) // แทรกส่วนของการแสดงคำแนะนำ .wrap( '<div class="input-group">' ) .before(settings.btnResetObj[k]) .after(settings.btnSpeakObj[k]); // สร้าง recognition object และกำหนด event handlers // (onstart , onerror, onend, onresult) var recognition = new webkitSpeechRecognition(); // สร้าง recognition object recognition.continuous = true; // กำหนด true ให้รับค่า จากเสียงไปเรื่อยๆ จนกว่าจะกดปุ่มหยุด recognition.interimResults = true; // แสดงข้อความช่วงจังหวะหรือไม่ กรณีพูดยาวๆ recognition.lang = settings.language; // กำหนดภาษา จากตัวแปรด้านบน recognition.onstart = function () { // เมื่อเกิดการเริ่มทำงานของการจดจำเสียง มาจากคำสั่ง recognition.start(); settings.recognizing = true; // เปลี่ยนค่าให้เริ่มทำการจดสับเสียงเป็น true เริ่มทำงาน settings.labelInstruction[k].slideDown( 'fast' ).html( 'กรุณาพูดช้าๆ และชัดเจน' ); // แสดงคำแนะนำ settings.btnSpeakObj[k].find( ".input-group-text" ).addClass( 'text-success' ); // เมื่อกดแล้วเปลี่ยนข้อความปุ่มเป็น คลิกอีกทีเพื่อหยุด หรือ Stop }; recognition.onerror = function (event) { // ถ้าเกิดข้อผิดพลาด ทำงานส่วนนี้ settings.labelInstruction[k].slideDown( 'fast' ).html( "There was a recognition error..." ); // แจ้งสถานะถ้าเกิดข้อผิดพลาด }; recognition.onend = function () { // ถ้าจบการทำงาน เช่นหยุดด้วยคำสั่ง recognition.stop(); // หรือไม่ได้พูดเพื่อใช้งาน การจดจำเสียงนาน ก็จะหยุดการทำงานเอง settings.recognizing = false; // กำหนดให้การจดจำเสียงอยูในสถานะหยุดการทำงาน settings.labelInstruction[k].html( 'Done' ).fadeOut(); // แสดงสถานะว่าเสร็จสิ้นแล้ว Done settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( 'text-danger text-warning text-success' ); // เปลี่ยนข้อความปุ่มกดให้เป็นค่าเริ่มต้น }; recognition.onresult = function (event) { // เมื่อแปลงเสียงเป็นข้อความสำเร็จ ส่งผลลัธ์กลับมา // ตัวแปรไว้เก็บข้อความในช่วงจังหวะหนึ่งจังหวะใดบางช่วง กรณีพูดยาวๆ var interim_transcript = '' ; // ปกติค่านี้ไม่ค่อยได้ใช้ จะใช้ค่า final มากกว่า // ถอดจากข้อความจาก array ผลลัพธ์ for ( var i = event.resultIndex; i < event.results.length; ++i) { // ถ้าเป็นค่าสุดท้ายแล้ว หยุดพูด หรือไม่ได้พูดต่อ if (event.results[i].isFinal) { // เอาข้อความผลัพธ์ที่ได้ มาต่อๆ กันและกับในตัวแปร final_transcript settings.final_transcript[k] += event.results[i][0].transcript+ ' ' ; } else { // ถ้าเป็นค่าข้อความระหว่างช่วงเวลา ในกรณีพูดยาวๆ เก็บในตัวแปร // เก็บในตัวแปร interim_transcript interim_transcript += event.results[i][0].transcript+ ' ' ; } } // บรรทัดที่ เอาไว้ทดสอบดูค่า ใน console ไม่ได้ใช้ปิดไป // console.log("interim: " + interim_transcript); // console.log("final: " + final_transcript); if (settings.final_transcript[k].length > 0) { // นับความยาวข้อความ ถ้ามากกว่า 0 แสดงว่ามีค่า // ตัวแปร final_transcript คือค่าข้อความที่ได้ เอาไปใช้งานต่อได้ settings.transcript[k].val(settings.final_transcript[k]); // แสดงค่าใน textarea } }; // กำหนดการทำงานปุ่ม เริ่มพิมพ์ด้วยเสียง settings.btnSpeakObj[k].click( function (e) { e.preventDefault(); // การจดจำเสียงกำลังทำงานอยู่หรือไม่ กดครั้งแรก จะยังไม่ทำงาน if (settings.recognizing) { // ภ้าทำงานอยู่ เมื่อกดก็จะเป็นหยุด recognition.stop(); // ให้หยุดการจัดจำเสียง settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( 'text-danger text-warning text-success' ); // เปลี่ยนข้อความปุ่มกดให้เป็นค่าเริ่มต้น settings.recognizing = false; // เปลี่ยนสถานะว่าหยุดทำงาน } else { // ถ้ากดแล้วสถานะการจดจำเสียงหยุดอยู่ ให้ทำงาน settings.final_transcript[k] = '' ; // กำหนดตัวแปรเก็บข้อความเป็นค่าว่างก่อน // ขออนุญาตใช้งานการจดจำเสียงและเริ่มใช้งาน recognition.start(); // แจ้งคำแนะนำว่าให้ กด allow หรือตกลง เพื่ออนุญาตให้ใช้งาน Microphone settings.labelInstruction[k].slideDown( 'fast' ).html( 'Allow the browser to use your Microphone' ); settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( "text-success" ).addClass( 'text-warning' ); // เปลี่ยนสีปุ่ม สำหรับรอคำสั่งเสียงพูด settings.transcript[k].val( '' ); // แสดงข้อความเป็นค่าว่าง } }); // ภ้ากดปุ่ม reset ข้อความ settings.btnResetObj[k].click( function (e) { e.preventDefault(); interim_transcript = '' ; settings.final_transcript[k] = '' ; settings.transcript[k].val( '' ); // แสดงข้อความเป็นค่าว่าง }); } }); }; })(jQuery); $( function (){ $( ".use-voice" ).type4me(); }); </script> |
ในส่วนของการกำหนด ตัวแปร option
1 2 3 4 5 6 7 8 9 10 | // ส่วนนี้ สำหรับกำหนดค่าเริ่มต้น var defaults={ recognizing: false , // กำหนดค่าเริ่มต้นการจดจำเสียง เริ่มต้น ให้เป็น false ไม่ทำงาน language: 'th-TH' , // กำหนดภาษา th-TH, btnSpeakObj:[], // สำหรับ object ปุ่มเริ่มพิมพ์ด้วยเสียง btnResetObj:[], // สำหรับ object ปุ่มรีเซ็ตข้อความ transcript:[], // สำหรับ object ที่เป็น input หรือ textarea ที่จะแสดงข้อความ labelInstruction:[], // สำหรับ object แสดงคำแนะนำ คำเตือน final_transcript:[], // ตัวแปร สำหรับเก็บข้อความที่แปลงจากเสียง }; |
ค่าเหล่านี้เป็นการกำหนดค่าเริ่มต้น โดยค่า recognizing และ language จะเป็นการกำหนดการเริ่มต้น
จำเสียงว่าทำงานทันทีหรือไม่ และการกำหนดภาษา ในที่นี้เป็น false และ 'th-TH' ตามลำดับ
ส่วนค่าที่เหลืออีก 5 อัน กำหนดเป็น array object สำหรับอ้างอิง
ต่อส่วนของการทำงานของ plugin และการตรวจสอบและใช้งาน web speech api
1 2 3 4 5 6 7 8 9 10 11 12 | /// คืนค่ากลับ การทำงานของ plugin return this .each( function (k, ele) { // กำหนดใช้งาน key หรือ index // ตรวจสอบ browser ว่าสนับสนุนการใช้งาน Speech API หรือไม่ if (!( 'webkitSpeechRecognition' in window)) { alert( "Your Browser does not support the Speech API" ); return false ; // หากไม่สามารถใช้งานได้ ให้ออกจากลูป } else { // ส่วนของโค้ด web speech api } }; |
ส่วนนี้จะตรวจสอบ ว่า บราวเซอร์รองรับการใช้งานหรือไม่ ถ้าไม่รองรับให้ทำการ return false; เพื่อออกจาก
ลูปของการจัดการ element ของคำสั่ง each()
ต่อไปส่่วนของการใช้งานโค้ด web speech api จะเริ่มที่ส่วนของการจัดการหน้าตา การกำหนด object ต่างๆ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // โค้ตสำหรับ การทำงานของ plugin settings.btnResetObj[k] = $(` <div class= "input-group-prepend" style= "cursor: pointer" > <span class= "input-group-text" ><i class= "far fa-times-circle" ></i></span> </div> `); settings.btnSpeakObj[k] = $(` <div class= "input-group-append" style= "cursor: pointer" > <span class= "input-group-text" ><i class= "fas fa-microphone" ></i></span> </div> `); settings.labelInstruction[k] = $( '<label class="instructions"></label>' ); settings.transcript[k] = $( this ); settings.labelInstruction[k].hide(); // ใช้ back qoute กดปุ่ม alt + 96 $( this ) .after(settings.labelInstruction[k]) // แทรกส่วนของการแสดงคำแนะนำ .wrap( '<div class="input-group">' ) .before(settings.btnResetObj[k]) .after(settings.btnSpeakObj[k]); |
ส่วนนี้ เรามีการเพิ่มส่วนของการแสดงคำแนะนำโดยใช้เป้น label เพิ่มเข้ามา โดยจะแสดงต่อจาก input
หรือ textarea ที่เรียกใช้งาน
และส่วนสุดท้าย เป็นส่วนของการทำงานของ web speech api รูปแบบโค้ดจะเป็นลักษณะคล้ายตัวเดิม
แต่ปรับเปลี่ยนในส่วนของการแสดงเล็กน้อย เช่นข้อความที่แสดง สีของปุ่มไมโครโฟนเมื่อรอรับคำสั่ง
ตัวแปรที่อ้างอิงต่างๆ เราเปลี่ยนไปตามค่าที่กำหนด
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | var recognition = new webkitSpeechRecognition(); // สร้าง recognition object recognition.continuous = true ; // กำหนด true ให้รับค่า จากเสียงไปเรื่อยๆ จนกว่าจะกดปุ่มหยุด recognition.interimResults = true ; // แสดงข้อความช่วงจังหวะหรือไม่ กรณีพูดยาวๆ recognition.lang = settings.language; // กำหนดภาษา จากตัวแปรด้านบน recognition.onstart = function () { // เมื่อเกิดการเริ่มทำงานของการจดจำเสียง มาจากคำสั่ง recognition.start(); settings.recognizing = true ; // เปลี่ยนค่าให้เริ่มทำการจดสับเสียงเป็น true เริ่มทำงาน settings.labelInstruction[k].slideDown( 'fast' ).html( 'กรุณาพูดช้าๆ และชัดเจน' ); // แสดงคำแนะนำ settings.btnSpeakObj[k].find( ".input-group-text" ).addClass( 'text-success' ); // เมื่อกดแล้วเปลี่ยนข้อความปุ่มเป็น คลิกอีกทีเพื่อหยุด หรือ Stop }; recognition.onerror = function (event) { // ถ้าเกิดข้อผิดพลาด ทำงานส่วนนี้ settings.labelInstruction[k].slideDown( 'fast' ).html( "There was a recognition error..." ); // แจ้งสถานะถ้าเกิดข้อผิดพลาด }; recognition.onend = function () { // ถ้าจบการทำงาน เช่นหยุดด้วยคำสั่ง recognition.stop(); // หรือไม่ได้พูดเพื่อใช้งาน การจดจำเสียงนาน ก็จะหยุดการทำงานเอง settings.recognizing = false ; // กำหนดให้การจดจำเสียงอยูในสถานะหยุดการทำงาน settings.labelInstruction[k].html( 'Done' ).fadeOut(); // แสดงสถานะว่าเสร็จสิ้นแล้ว Done settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( 'text-danger text-warning text-success' ); // เปลี่ยนข้อความปุ่มกดให้เป็นค่าเริ่มต้น }; recognition.onresult = function (event) { // เมื่อแปลงเสียงเป็นข้อความสำเร็จ ส่งผลลัธ์กลับมา // ตัวแปรไว้เก็บข้อความในช่วงจังหวะหนึ่งจังหวะใดบางช่วง กรณีพูดยาวๆ var interim_transcript = '' ; // ปกติค่านี้ไม่ค่อยได้ใช้ จะใช้ค่า final มากกว่า // ถอดจากข้อความจาก array ผลลัพธ์ for ( var i = event.resultIndex; i < event.results.length; ++i) { // ถ้าเป็นค่าสุดท้ายแล้ว หยุดพูด หรือไม่ได้พูดต่อ if (event.results[i].isFinal) { // เอาข้อความผลัพธ์ที่ได้ มาต่อๆ กันและกับในตัวแปร final_transcript settings.final_transcript[k] += event.results[i][0].transcript+ ' ' ; } else { // ถ้าเป็นค่าข้อความระหว่างช่วงเวลา ในกรณีพูดยาวๆ เก็บในตัวแปร // เก็บในตัวแปร interim_transcript interim_transcript += event.results[i][0].transcript+ ' ' ; } } // บรรทัดที่ เอาไว้ทดสอบดูค่า ใน console ไม่ได้ใช้ปิดไป // console.log("interim: " + interim_transcript); // console.log("final: " + final_transcript); if (settings.final_transcript[k].length > 0) { // นับความยาวข้อความ ถ้ามากกว่า 0 แสดงว่ามีค่า // ตัวแปร final_transcript คือค่าข้อความที่ได้ เอาไปใช้งานต่อได้ settings.transcript[k].val(settings.final_transcript[k]); // แสดงค่าใน textarea } }; // กำหนดการทำงานปุ่ม เริ่มพิมพ์ด้วยเสียง settings.btnSpeakObj[k].click( function (e) { e.preventDefault(); // การจดจำเสียงกำลังทำงานอยู่หรือไม่ กดครั้งแรก จะยังไม่ทำงาน if (settings.recognizing) { // ภ้าทำงานอยู่ เมื่อกดก็จะเป็นหยุด recognition.stop(); // ให้หยุดการจัดจำเสียง settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( 'text-danger text-warning text-success' ); // เปลี่ยนข้อความปุ่มกดให้เป็นค่าเริ่มต้น settings.recognizing = false ; // เปลี่ยนสถานะว่าหยุดทำงาน } else { // ถ้ากดแล้วสถานะการจดจำเสียงหยุดอยู่ ให้ทำงาน settings.final_transcript[k] = '' ; // กำหนดตัวแปรเก็บข้อความเป็นค่าว่างก่อน // ขออนุญาตใช้งานการจดจำเสียงและเริ่มใช้งาน recognition.start(); // แจ้งคำแนะนำว่าให้ กด allow หรือตกลง เพื่ออนุญาตให้ใช้งาน Microphone settings.labelInstruction[k].slideDown( 'fast' ).html( 'Allow the browser to use your Microphone' ); settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( "text-success" ).addClass( 'text-warning' ); // เปลี่ยนสีปุ่ม สำหรับรอคำสั่งเสียงพูด settings.transcript[k].val( '' ); // แสดงข้อความเป็นค่าว่าง } }); // ภ้ากดปุ่ม reset ข้อความ settings.btnResetObj[k].click( function (e) { e.preventDefault(); interim_transcript = '' ; settings.final_transcript[k] = '' ; settings.transcript[k].val( '' ); // แสดงข้อความเป็นค่าว่าง }); |
ทดสอบการทำงานได้ที่ DEMO 1 อย่าลืมอนุญาตให้ใช้งาน microphone เมื่อบราวเซอร์แจ้งเตือน หรือกรณี block ไปแล้วให้ทำการ
คลิกที่ปุ่ม ลูกกุญแจ ที่ address bar ของบราวเซอร์ แล้วเลือกอนุญาตใช้งาน microphone
เราสามารถแยกไฟล์ plugin เป็นอีกไฟล์สำหรับเรียกใช้งานได้ สมมติเป็น jquery.type4me.js
ไฟล์ jquery.type4me.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | // JavaScript Document ( function ($){ // type4me คือชื่อของ plugin ที่เราต้องการ $.fn.type4me = function ( options ) { // กำหนดให้ plugin ของเราสามารถ รับค่าเพิ่มเติมได้ มี options // ส่วนนี้ สำหรับกำหนดค่าเริ่มต้น var defaults={ recognizing: false , // กำหนดค่าเริ่มต้นการจดจำเสียง เริ่มต้น ให้เป็น false ไม่ทำงาน language: 'th-TH' , // กำหนดภาษา th-TH, btnSpeakObj:[], // สำหรับ object ปุ่มเริ่มพิมพ์ด้วยเสียง btnResetObj:[], // สำหรับ object ปุ่มรีเซ็ตข้อความ transcript:[], // สำหรับ object ที่เป็น input หรือ textarea ที่จะแสดงข้อความ labelInstruction:[], // สำหรับ object แสดงคำแนะนำ คำเตือน final_transcript:[], // ตัวแปร สำหรับเก็บข้อความที่แปลงจากเสียง }; // ส่วนสำหรับ เป็นต้วแปร รับค่า options หากมี หรือใช้ค่าเริ่มต้น ถ้ากำหนด var settings = $.extend( {}, defaults, options ); /// คืนค่ากลับ การทำงานของ plugin return this .each( function (k, ele) { // กำหนดใช้งาน key หรือ index // ตรวจสอบ browser ว่าสนับสนุนการใช้งาน Speech API หรือไม่ if (!( 'webkitSpeechRecognition' in window)) { alert( "Your Browser does not support the Speech API" ); return false ; // หากไม่สามารถใช้งานได้ ให้ออกจากลูป } else { // โค้ตสำหรับ การทำงานของ plugin settings.btnResetObj[k] = $(` <div class= "input-group-prepend" style= "cursor: pointer" > <span class= "input-group-text" ><i class= "far fa-times-circle" ></i></span> </div> `); settings.btnSpeakObj[k] = $(` <div class= "input-group-append" style= "cursor: pointer" > <span class= "input-group-text" ><i class= "fas fa-microphone" ></i></span> </div> `); settings.labelInstruction[k] = $( '<label class="instructions"></label>' ); settings.transcript[k] = $( this ); settings.labelInstruction[k].hide(); // ใช้ back qoute กดปุ่ม alt + 96 $( this ) .after(settings.labelInstruction[k]) // แทรกส่วนของการแสดงคำแนะนำ .wrap( '<div class="input-group">' ) .before(settings.btnResetObj[k]) .after(settings.btnSpeakObj[k]); // สร้าง recognition object และกำหนด event handlers // (onstart , onerror, onend, onresult) var recognition = new webkitSpeechRecognition(); // สร้าง recognition object recognition.continuous = true ; // กำหนด true ให้รับค่า จากเสียงไปเรื่อยๆ จนกว่าจะกดปุ่มหยุด recognition.interimResults = true ; // แสดงข้อความช่วงจังหวะหรือไม่ กรณีพูดยาวๆ recognition.lang = settings.language; // กำหนดภาษา จากตัวแปรด้านบน recognition.onstart = function () { // เมื่อเกิดการเริ่มทำงานของการจดจำเสียง มาจากคำสั่ง recognition.start(); settings.recognizing = true ; // เปลี่ยนค่าให้เริ่มทำการจดสับเสียงเป็น true เริ่มทำงาน settings.labelInstruction[k].slideDown( 'fast' ).html( 'กรุณาพูดช้าๆ และชัดเจน' ); // แสดงคำแนะนำ settings.btnSpeakObj[k].find( ".input-group-text" ).addClass( 'text-success' ); // เมื่อกดแล้วเปลี่ยนข้อความปุ่มเป็น คลิกอีกทีเพื่อหยุด หรือ Stop }; recognition.onerror = function (event) { // ถ้าเกิดข้อผิดพลาด ทำงานส่วนนี้ settings.labelInstruction[k].slideDown( 'fast' ).html( "There was a recognition error..." ); // แจ้งสถานะถ้าเกิดข้อผิดพลาด }; recognition.onend = function () { // ถ้าจบการทำงาน เช่นหยุดด้วยคำสั่ง recognition.stop(); // หรือไม่ได้พูดเพื่อใช้งาน การจดจำเสียงนาน ก็จะหยุดการทำงานเอง settings.recognizing = false ; // กำหนดให้การจดจำเสียงอยูในสถานะหยุดการทำงาน settings.labelInstruction[k].html( 'Done' ).fadeOut(); // แสดงสถานะว่าเสร็จสิ้นแล้ว Done settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( 'text-danger text-warning text-success' ); // เปลี่ยนข้อความปุ่มกดให้เป็นค่าเริ่มต้น }; recognition.onresult = function (event) { // เมื่อแปลงเสียงเป็นข้อความสำเร็จ ส่งผลลัธ์กลับมา // ตัวแปรไว้เก็บข้อความในช่วงจังหวะหนึ่งจังหวะใดบางช่วง กรณีพูดยาวๆ var interim_transcript = '' ; // ปกติค่านี้ไม่ค่อยได้ใช้ จะใช้ค่า final มากกว่า // ถอดจากข้อความจาก array ผลลัพธ์ for ( var i = event.resultIndex; i < event.results.length; ++i) { // ถ้าเป็นค่าสุดท้ายแล้ว หยุดพูด หรือไม่ได้พูดต่อ if (event.results[i].isFinal) { // เอาข้อความผลัพธ์ที่ได้ มาต่อๆ กันและกับในตัวแปร final_transcript settings.final_transcript[k] += event.results[i][0].transcript+ ' ' ; } else { // ถ้าเป็นค่าข้อความระหว่างช่วงเวลา ในกรณีพูดยาวๆ เก็บในตัวแปร // เก็บในตัวแปร interim_transcript interim_transcript += event.results[i][0].transcript+ ' ' ; } } // บรรทัดที่ เอาไว้ทดสอบดูค่า ใน console ไม่ได้ใช้ปิดไป // console.log("interim: " + interim_transcript); // console.log("final: " + final_transcript); if (settings.final_transcript[k].length > 0) { // นับความยาวข้อความ ถ้ามากกว่า 0 แสดงว่ามีค่า // ตัวแปร final_transcript คือค่าข้อความที่ได้ เอาไปใช้งานต่อได้ settings.transcript[k].val(settings.final_transcript[k]); // แสดงค่าใน textarea } }; // กำหนดการทำงานปุ่ม เริ่มพิมพ์ด้วยเสียง settings.btnSpeakObj[k].click( function (e) { e.preventDefault(); // การจดจำเสียงกำลังทำงานอยู่หรือไม่ กดครั้งแรก จะยังไม่ทำงาน if (settings.recognizing) { // ภ้าทำงานอยู่ เมื่อกดก็จะเป็นหยุด recognition.stop(); // ให้หยุดการจัดจำเสียง settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( 'text-danger text-warning text-success' ); // เปลี่ยนข้อความปุ่มกดให้เป็นค่าเริ่มต้น settings.recognizing = false ; // เปลี่ยนสถานะว่าหยุดทำงาน } else { // ถ้ากดแล้วสถานะการจดจำเสียงหยุดอยู่ ให้ทำงาน settings.final_transcript[k] = '' ; // กำหนดตัวแปรเก็บข้อความเป็นค่าว่างก่อน // ขออนุญาตใช้งานการจดจำเสียงและเริ่มใช้งาน recognition.start(); // แจ้งคำแนะนำว่าให้ กด allow หรือตกลง เพื่ออนุญาตให้ใช้งาน Microphone settings.labelInstruction[k].slideDown( 'fast' ).html( 'Allow the browser to use your Microphone' ); settings.btnSpeakObj[k].find( ".input-group-text" ) .removeClass( "text-success" ).addClass( 'text-warning' ); // เปลี่ยนสีปุ่ม สำหรับรอคำสั่งเสียงพูด settings.transcript[k].val( '' ); // แสดงข้อความเป็นค่าว่าง } }); // ภ้ากดปุ่ม reset ข้อความ settings.btnResetObj[k].click( function (e) { e.preventDefault(); interim_transcript = '' ; settings.final_transcript[k] = '' ; settings.transcript[k].val( '' ); // แสดงข้อความเป็นค่าว่าง }); } }); }; })(jQuery); |
แล้วเรียกใช้งานเป็น
1 2 3 4 5 6 7 8 9 10 | <input type= "text" class = "form-control use-voice" name= "news_title" autocomplete= "off" value= "" required> <script src= "jquery.type4me.js" ></script> <script type= "text/javascript" > $( function (){ $( ".use-voice" ).type4me(); }); </script> |
หรือสร้างเป็น JavaScript Minifier เพื่อให้มีขนาดเล็กไว้ใช้งานก็ได้ ดาวน์โหลด
วิธีใช้งาน
1 2 3 4 5 6 7 8 9 10 | <input type= "text" class = "form-control use-voice" name= "news_title" autocomplete= "off" value= "" required> <script src= "jquery.type4me.min.js" ></script> <script type= "text/javascript" > $( function (){ $( ".use-voice" ).type4me(); }); </script> |
เราสามารถปรับให้รองรับภาษา สมมติเป็นจีน ได้ดังนี้
1 2 3 4 5 6 7 | <script type= "text/javascript" > $( function (){ $( ".use-voice" ).type4me({ }); }); </script> |
ตัวอย่างผลลัพธ์

ทดสอบพูดภาษาจีนกลาง ใน DEMO 2
หวังว่าเนื้อหา และแนวทางข้างต้น จะเป็นประโยชน์สำหรับประยุกต์ใช้งานต่อไปได้