วิธีการสร้างตัวแก้ไขการสังเคราะห์เสียง
เผยแพร่แล้ว: 2022-03-10เมื่อสตีฟจ็อบส์เปิดตัว Macintosh ในปี 1984 มันพูดว่า "สวัสดี" สำหรับเราจากเวที แม้กระทั่ง ณ จุดนั้น การสังเคราะห์เสียงพูดก็ไม่ใช่เทคโนโลยีใหม่จริงๆ: Bell Labs พัฒนาตัวขับเสียงให้เร็วที่สุดเท่าที่จะเป็นช่วงปลายทศวรรษที่ 30 และแนวคิดของคอมพิวเตอร์ระบบสั่งงานด้วยเสียงทำให้ผู้คนตระหนักรู้เมื่อ Stanley Kubrick ให้เสียงพากย์เป็น HAL9000 ใน ปี 2544: A Space Odyssey (1968)
ก่อนหน้าที่จะมีการเปิดตัว Siri, Amazon Echo และ Google Assistant ของ Apple ในช่วงกลางปี 2015 อินเทอร์เฟซเสียงได้ค้นพบหนทางสู่บ้าน ข้อมือ และกระเป๋าของสาธารณชนในวงกว้าง เรายังอยู่ในช่วงการรับเลี้ยงบุตรบุญธรรม แต่ดูเหมือนว่าผู้ช่วยเสียงเหล่านี้จะอยู่ที่นี่ต่อไป
กล่าว คือ เว็บไม่ได้เป็นเพียงข้อความแบบพาสซีฟบนหน้าจออีกต่อไป บรรณาธิการเว็บและนักออกแบบ UX ต้องทำความคุ้นเคยกับการสร้างเนื้อหาและบริการที่ควรพูดออกมาดัง ๆ
เรากำลังดำเนินการอย่างรวดเร็วไปสู่การใช้ระบบจัดการเนื้อหาที่ช่วยให้เราทำงานกับเนื้อหาของเราแบบไม่มีหัวเรื่องและผ่าน API ได้ ส่วนสุดท้ายคือการสร้างอินเทอร์เฟซสำหรับบรรณาธิการที่ช่วยให้ปรับแต่งเนื้อหาสำหรับเสียงได้ง่ายขึ้น ลองทำกันดูนะ!
SSML . คืออะไร
ในขณะที่เว็บเบราว์เซอร์ใช้ข้อกำหนดของ W3C สำหรับ HyperText Markup Language (HTML) เพื่อแสดงเอกสารด้วยสายตา ผู้ช่วยเสียงส่วนใหญ่ใช้ Speech Synthesis Markup Language (SSML) เมื่อสร้างคำพูด
ตัวอย่างน้อยที่สุดโดยใช้องค์ประกอบราก <speak>
และแท็กย่อหน้า ( <p>
) และประโยค ( <s>
)
<speak> <p> <s>This is the first sentence of the paragraph.</s> <s>Here's another sentence.</s> </p> </speak>
โดยที่ SSML มีอยู่คือเมื่อเราแนะนำแท็กสำหรับ <emphasis>
และ <prosody>
(pitch):
<speak> <p> <s>Put some <emphasis strength="strong">extra weight on these words</emphasis></s> <s>And say <prosody pitch="high" rate="fast">this a bit higher and faster</prosody>!</s> </p> </speak>
SSML มีคุณสมบัติมากกว่า แต่นี่เพียงพอที่จะทำความเข้าใจพื้นฐาน ตอนนี้ มาดูตัวแก้ไขที่เราจะใช้เพื่อสร้างอินเทอร์เฟซการแก้ไขการสังเคราะห์เสียงอย่างละเอียด
ตัวแก้ไขสำหรับข้อความพกพา
ในการสร้างตัวแก้ไขนี้ เราจะใช้ตัวแก้ไขสำหรับ Portable Text ที่มีใน Sanity.io Portable Text เป็นข้อกำหนด JSON สำหรับการแก้ไข Rich Text ที่สามารถจัดลำดับเป็นภาษามาร์กอัปใดๆ เช่น SSML ซึ่งหมายความว่าคุณสามารถใช้ตัวอย่างข้อความเดียวกันในที่ต่างๆ ได้อย่างง่ายดายโดยใช้ภาษามาร์กอัปที่ต่างกัน
ติดตั้งสุขาภิบาล
Sanity.io เป็นแพลตฟอร์มสำหรับเนื้อหาที่มีโครงสร้างซึ่งมาพร้อมกับสภาพแวดล้อมการแก้ไขโอเพนซอร์สที่สร้างด้วย React.js ใช้เวลาสองนาทีในการเริ่มต้นใช้งาน
พิมพ์ npm i -g @sanity/cli && sanity init
ลงในเทอร์มินัลแล้วทำตามคำแนะนำ เลือก "ว่าง" เมื่อคุณได้รับพร้อมท์สำหรับเทมเพลตโครงการ
หากคุณไม่ต้องการทำตามบทช่วยสอนนี้และสร้างตัวแก้ไขนี้ตั้งแต่ต้น คุณยังสามารถโคลนโค้ดของบทช่วยสอนนี้และทำตามคำแนะนำใน README.md
เมื่อดาวน์โหลดเอดิเตอร์แล้ว คุณจะเรียกใช้ sanity start
ในโฟลเดอร์โปรเจ็กต์เพื่อเริ่มใช้งาน มันจะเริ่มต้นเซิร์ฟเวอร์การพัฒนาที่ใช้ Hot Module Reloading เพื่ออัปเดตการเปลี่ยนแปลงเมื่อคุณแก้ไขไฟล์
วิธีกำหนดค่าแบบแผนใน Sanity Studio
การสร้างไฟล์ตัวแก้ไข
เราจะเริ่มต้นด้วยการสร้างโฟลเดอร์ชื่อ ssml-editor ในโฟลเดอร์ /schema ในโฟลเดอร์นั้น เราจะใส่ไฟล์เปล่าบางไฟล์:
/ssml-tutorial/schemas/ssml-editor ├── alias.js ├── emphasis.js ├── annotations.js ├── preview.js ├── prosody.js ├── sayAs.js ├── blocksToSSML.js ├── speech.js ├── SSMLeditor.css └── SSMLeditor.js
ตอนนี้ เราสามารถเพิ่มสคีมาเนื้อหาในไฟล์เหล่านี้ได้ ส คีมาเนื้อหา คือสิ่งที่กำหนดโครงสร้างข้อมูลสำหรับ Rich Text และสิ่งที่ Sanity Studio ใช้เพื่อสร้างอินเทอร์เฟซด้านบรรณาธิการ เป็นออบเจ็กต์ JavaScript อย่างง่ายที่ส่วนใหญ่ต้องการเพียง name
และ type
นอกจากนี้เรายังสามารถเพิ่ม title
และ description
เพื่อทำให้บรรณาธิการดีขึ้นเล็กน้อย ตัวอย่างเช่น นี่คือสคีมาสำหรับฟิลด์ข้อความธรรมดาสำหรับ title
:
export default { name: 'title', type: 'string', title: 'Title', description: 'Titles should be short and descriptive' }
Portable Text สร้างขึ้นจากแนวคิดของ Rich Text เป็นข้อมูล สิ่งนี้มีประสิทธิภาพเพราะช่วยให้คุณสืบค้นข้อความที่มีการจัดรูปแบบและแปลงเป็นมาร์กอัปใดก็ได้ที่คุณต้องการ
เป็นอาร์เรย์ของอ็อบเจ็กต์ที่เรียกว่า "บล็อก" ซึ่งคุณอาจมองว่าเป็น "ย่อหน้า" ในบล็อกมีช่วงเด็กจำนวนมาก แต่ละบล็อกสามารถมีสไตล์และชุดของคำจำกัดความของเครื่องหมาย ซึ่งอธิบายโครงสร้างข้อมูลที่กระจายตามช่วงย่อย
Sanity.io มาพร้อมกับโปรแกรมแก้ไขที่สามารถอ่านและเขียนข้อความแบบพกพาได้ และเปิดใช้งานโดยการวางประเภท block
ไว้ในฟิลด์ array
ดังนี้:
// speech.js export default { name: 'speech', type: 'array', title: 'SSML Editor', of: [ { type: 'block' } ] }
อาร์เรย์สามารถมีได้หลายประเภท สำหรับตัวแก้ไข SSML สิ่งเหล่านี้อาจเป็นบล็อกสำหรับไฟล์เสียง แต่นั่นอยู่นอกขอบเขตของบทช่วยสอนนี้
สิ่งสุดท้ายที่เราต้องการทำคือการเพิ่มประเภทเนื้อหาที่สามารถใช้ตัวแก้ไขนี้ได้ ผู้ช่วยส่วนใหญ่ใช้โมเดลเนื้อหาอย่างง่ายของ "ความตั้งใจ" และ "การปฏิบัติตาม":
- ความตั้งใจ
โดยปกติแล้วจะเป็นรายการสตริงที่โมเดล AI ใช้เพื่อกำหนดสิ่งที่ผู้ใช้ต้องการทำให้เสร็จ - การปฏิบัติตาม
สิ่งนี้จะเกิดขึ้นเมื่อมีการระบุ "เจตนา" การปฏิบัติตามมักจะ - หรืออย่างน้อย - มาพร้อมกับการตอบสนองบางอย่าง
เรามาสร้างประเภทเนื้อหาอย่างง่ายที่เรียกว่า fulfillment
ซึ่งใช้ตัวแก้ไขการสังเคราะห์เสียงพูด สร้างไฟล์ใหม่ชื่อ Fulfillment.js และบันทึกไว้ในโฟลเดอร์ /schema :
// fulfillment.js export default { name: 'fulfillment', type: 'document', title: 'Fulfillment', of: [ { name: 'title', type: 'string', title: 'Title', description: 'Titles should be short and descriptive' }, { name: 'response', type: 'speech' } ] }
บันทึกไฟล์ และเปิด schema.js เพิ่มลงในสตูดิโอของคุณดังนี้:
// schema.js import createSchema from 'part:@sanity/base/schema-creator' import schemaTypes from 'all:part:@sanity/base/schema-type' import fullfillment from './fullfillment' import speech from './speech' export default createSchema({ name: 'default', types: schemaTypes.concat([ fullfillment, speech, ]) })
หากคุณเรียกใช้ sanity start
ในส่วนติดต่อบรรทัดคำสั่งภายในโฟลเดอร์รูทของโปรเจ็กต์ สตูดิโอจะเริ่มต้นในเครื่อง และคุณจะเพิ่มรายการสำหรับการเติมเต็มได้ คุณสามารถให้สตูดิโอทำงานต่อไปได้ในขณะที่เราดำเนินการต่อ เนื่องจากจะโหลดซ้ำอัตโนมัติพร้อมการเปลี่ยนแปลงใหม่เมื่อคุณบันทึกไฟล์
การเพิ่ม SSML ให้กับตัวแก้ไข
ตามค่าเริ่มต้น ประเภทของ block
จะให้ตัวแก้ไขมาตรฐานสำหรับ Rich Text ที่เน้นการมองเห็นด้วยรูปแบบส่วนหัว สไตล์มัณฑนากรสำหรับการเน้นและเน้นย้ำ คำอธิบายประกอบสำหรับลิงก์ และรายการ ตอนนี้ เราต้องการแทนที่สิ่งเหล่านั้นด้วยแนวคิดเกี่ยวกับเสียงที่พบใน SSML
เราเริ่มต้นด้วยการกำหนดโครงสร้างเนื้อหาต่างๆ พร้อมคำอธิบายที่เป็นประโยชน์สำหรับตัวแก้ไข ซึ่งเราจะเพิ่มไปยัง block
ใน SSMLeditorSchema.js เป็นการกำหนดค่าสำหรับ annotations
นั่นคือ "เน้น", "นามแฝง", "ฉันทลักษณ์" และ "พูดในฐานะ"
เน้น
เราเริ่มต้นด้วย "การเน้น" ซึ่งควบคุมน้ำหนักของข้อความที่ทำเครื่องหมายไว้ เรากำหนดให้เป็นสตริงที่มีรายการค่าที่กำหนดไว้ล่วงหน้าซึ่งผู้ใช้สามารถเลือกได้:
// emphasis.js export default { name: 'emphasis', type: 'object', title: 'Emphasis', description: 'The strength of the emphasis put on the contained text', fields: [ { name: 'level', type: 'string', options: { list: [ { value: 'strong', title: 'Strong' }, { value: 'moderate', title: 'Moderate' }, { value: 'none', title: 'None' }, { value: 'reduced', title: 'Reduced' } ] } } ] }
นามแฝง
บางครั้งคำที่เขียนและคำพูดต่างกัน ตัวอย่างเช่น คุณต้องการใช้ตัวย่อของวลีในข้อความที่เขียน แต่ให้อ่านออกเสียงทั้งวลี ตัวอย่างเช่น:
<s>This is a <sub alias="Speech Synthesis Markup Language">SSML</sub> tutorial</s>
ฟิลด์อินพุตสำหรับนามแฝงเป็นสตริงอย่างง่าย:
// alias.js export default { name: 'alias', type: 'object', title: 'Alias (sub)', description: 'Replaces the contained text for pronunciation. This allows a document to contain both a spoken and written form.', fields: [ { name: 'text', type: 'string', title: 'Replacement text', } ] }
ฉันทลักษณ์
ด้วยคุณสมบัติฉันทลักษณ์ เราสามารถควบคุมแง่มุมต่างๆ ว่าควรพูดข้อความอย่างไร เช่น ระดับเสียงสูงต่ำ อัตรา และระดับเสียง มาร์กอัปสำหรับสิ่งนี้สามารถมีลักษณะดังนี้:
<s>Say this with an <prosody pitch="x-low">extra low pitch</prosody>, and this <prosody rate="fast" volume="loud">loudly with a fast rate</prosody></s>
อินพุตนี้จะมีสามฟิลด์พร้อมตัวเลือกสตริงที่กำหนดไว้ล่วงหน้า:
// prosody.js export default { name: 'prosody', type: 'object', title: 'Prosody', description: 'Control of the pitch, speaking rate, and volume', fields: [ { name: 'pitch', type: 'string', title: 'Pitch', description: 'The baseline pitch for the contained text', options: { list: [ { value: 'x-low', title: 'Extra low' }, { value: 'low', title: 'Low' }, { value: 'medium', title: 'Medium' }, { value: 'high', title: 'High' }, { value: 'x-high', title: 'Extra high' }, { value: 'default', title: 'Default' } ] } }, { name: 'rate', type: 'string', title: 'Rate', description: 'A change in the speaking rate for the contained text', options: { list: [ { value: 'x-slow', title: 'Extra slow' }, { value: 'slow', title: 'Slow' }, { value: 'medium', title: 'Medium' }, { value: 'fast', title: 'Fast' }, { value: 'x-fast', title: 'Extra fast' }, { value: 'default', title: 'Default' } ] } }, { name: 'volume', type: 'string', title: 'Volume', description: 'The volume for the contained text.', options: { list: [ { value: 'silent', title: 'Silent' }, { value: 'x-soft', title: 'Extra soft' }, { value: 'medium', title: 'Medium' }, { value: 'loud', title: 'Loud' }, { value: 'x-loud', title: 'Extra loud' }, { value: 'default', title: 'Default' } ] } } ] }
พูดเป็น
สุดท้ายที่เราต้องการรวมคือ <say-as>
แท็กนี้ช่วยให้เราควบคุมวิธีการออกเสียงข้อมูลบางอย่างได้มากขึ้น เรายังสามารถใช้มันเพื่อแยกคำหากคุณต้องการแก้ไขบางสิ่งในอินเทอร์เฟซเสียง นั่นคือ @!%& มีประโยชน์!
<s>Do I have to <say-as interpret-as="expletive">frakking</say-as> <say-as interpret-as="verbatim">spell</say-as> it out for you!?</s>
// sayAs.js export default { name: 'sayAs', type: 'object', title: 'Say as...', description: 'Lets you indicate information about the type of text construct that is contained within the element. It also helps specify the level of detail for rendering the contained text.', fields: [ { name: 'interpretAs', type: 'string', title: 'Interpret as...', options: { list: [ { value: 'cardinal', title: 'Cardinal numbers' }, { value: 'ordinal', title: 'Ordinal numbers (1st, 2nd, 3th...)' }, { value: 'characters', title: 'Spell out characters' }, { value: 'fraction', title: 'Say numbers as fractions' }, { value: 'expletive', title: 'Blip out this word' }, { value: 'unit', title: 'Adapt unit to singular or plural' }, { value: 'verbatim', title: 'Spell out letter by letter (verbatim)' }, { value: 'date', title: 'Say as a date' }, { value: 'telephone', title: 'Say as a telephone number' } ] } }, { name: 'date', type: 'object', title: 'Date', fields: [ { name: 'format', type: 'string', description: 'The format attribute is a sequence of date field character codes. Supported field character codes in format are {y, m, d} for year, month, and day (of the month) respectively. If the field code appears once for year, month, or day then the number of digits expected are 4, 2, and 2 respectively. If the field code is repeated then the number of expected digits is the number of times the code is repeated. Fields in the date text may be separated by punctuation and/or spaces.' }, { name: 'detail', type: 'number', validation: Rule => Rule.required() .min(0) .max(2), description: 'The detail attribute controls the spoken form of the date. For detail='1' only the day fields and one of month or year fields are required, although both may be supplied' } ] } ] }
ตอนนี้ เราสามารถนำเข้าสิ่งเหล่านี้ในไฟล์ annotations.js ซึ่งทำให้ทุกอย่างเป็นระเบียบขึ้น
// annotations.js export {default as alias} from './alias' export {default as emphasis} from './emphasis' export {default as prosody} from './prosody' export {default as sayAs} from './sayAs'
ตอนนี้ เราสามารถนำเข้าประเภทคำอธิบายประกอบเหล่านี้ไปยังสคีมาหลักของเราได้แล้ว:
// schema.js import createSchema from "part:@sanity/base/schema-creator" import schemaTypes from "all:part:@sanity/base/schema-type" import fulfillment from './fulfillment' import speech from './ssml-editor/speech' import { alias, emphasis, prosody, sayAs } from './annotations' export default createSchema({ name: "default", types: schemaTypes.concat([ fulfillment, speech, alias, emphasis, prosody, sayAs ]) })
สุดท้ายนี้ เราสามารถเพิ่มสิ่งเหล่านี้ลงในตัวแก้ไขได้ดังนี้:
// speech.js export default { name: 'speech', type: 'array', title: 'SSML Editor', of: [ { type: 'block', styles: [], lists: [], marks: { decorators: [], annotations: [ {type: 'alias'}, {type: 'emphasis'}, {type: 'prosody'}, {type: 'sayAs'} ] } } ] }
สังเกตว่าเรายังเพิ่มอาร์เรย์ว่างให้กับ styles
และ decorators
สิ่งนี้จะปิดใช้งานสไตล์เริ่มต้นและตัวตกแต่ง (เช่น ตัวหนาและตัวเน้น) เนื่องจากไม่สมเหตุสมผลในกรณีนี้
ปรับแต่งรูปลักษณ์และความรู้สึก
ตอนนี้ เรามีฟังก์ชันอยู่แล้ว แต่เนื่องจากเราไม่ได้ระบุไอคอนใดๆ คำอธิบายประกอบแต่ละรายการจะใช้ไอคอนเริ่มต้น ซึ่งทำให้ตัวแก้ไขใช้งานยากสำหรับผู้เขียนจริงๆ มาแก้ไขกันเถอะ!
ด้วยตัวแก้ไขสำหรับ Portable Text คุณสามารถฉีดส่วนประกอบ React ทั้งสำหรับไอคอนและสำหรับวิธีการแสดงข้อความที่ทำเครื่องหมายไว้ ในที่นี้ เราจะปล่อยให้อีโมจิทำงานแทนเรา แต่แน่นอนว่าคุณทำได้ไกลด้วยสิ่งนี้ ทำให้พวกมันมีพลังและอื่นๆ สำหรับ prosody
เราจะเปลี่ยนไอคอนตามระดับเสียงที่เลือก โปรดทราบว่าฉันละเว้นฟิลด์ในตัวอย่างเหล่านี้เพื่อความกระชับ คุณ ไม่ควร ลบฟิลด์เหล่านี้ในไฟล์ในเครื่องของคุณ
// alias.js import React from 'react' export default { name: 'alias', type: 'object', title: 'Alias (sub)', description: 'Replaces the contained text for pronunciation. This allows a document to contain both a spoken and written form.', fields: [ /* all the fields */ ], blockEditor: { icon: () => '', render: ({ children }) => <span>{children} </span>, }, };
// emphasis.js import React from 'react' export default { name: 'emphasis', type: 'object', title: 'Emphasis', description: 'The strength of the emphasis put on the contained text', fields: [ /* all the fields */ ], blockEditor: { icon: () => '', render: ({ children }) => <span>{children} </span>, }, };
// prosody.js import React from 'react' export default { name: 'prosody', type: 'object', title: 'Prosody', description: 'Control of the pitch, speaking rate, and volume', fields: [ /* all the fields */ ], blockEditor: { icon: () => '', render: ({ children, volume }) => ( <span> {children} {['x-loud', 'loud'].includes(volume) ? '' : ''} </span> ), }, };
// sayAs.js import React from 'react' export default { name: 'sayAs', type: 'object', title: 'Say as...', description: 'Lets you indicate information about the type of text construct that is contained within the element. It also helps specify the level of detail for rendering the contained text.', fields: [ /* all the fields */ ], blockEditor: { icon: () => '', render: props => <span>{props.children} </span>, }, };
ตอนนี้คุณมีตัวแก้ไขสำหรับแก้ไขข้อความที่ผู้ช่วยเสียงสามารถใช้ได้ แต่จะไม่ค่อยมีประโยชน์นักหรอกหรือถ้าผู้แก้ไขสามารถดูตัวอย่างได้ว่าข้อความจริงๆ จะเป็นอย่างไร
การเพิ่มปุ่มแสดงตัวอย่างโดยใช้ Text-to-Speech ของ Google
จริง ๆ แล้วการสนับสนุนการสังเคราะห์เสียงพูดนั้นกำลังมาถึงสำหรับเบราว์เซอร์ แต่ในบทช่วยสอนนี้ เราจะใช้ Text-to-Speech API ของ Google ซึ่งรองรับ SSML การสร้างฟังก์ชันการแสดงตัวอย่างนี้จะเป็นการสาธิตวิธีจัดลำดับข้อความแบบพกพาลงใน SSML ในบริการใดก็ตามที่คุณต้องการใช้
การห่อตัวแก้ไขในส่วนประกอบที่ทำปฏิกิริยา
เราเริ่มต้นด้วยการเปิดไฟล์ SSMLeditor.js และเพิ่มรหัสต่อไปนี้:
// SSMLeditor.js import React, { Fragment } from 'react'; import { BlockEditor } from 'part:@sanity/form-builder'; export default function SSMLeditor(props) { return ( <Fragment> <BlockEditor {...props} /> </Fragment> ); }
ตอนนี้เราได้รวมตัวแก้ไขไว้ในองค์ประกอบ React ของเราเองแล้ว อุปกรณ์ประกอบฉากทั้งหมดที่จำเป็น รวมถึงข้อมูลที่มีอยู่ในนั้นจะถูกส่งต่อในแบบเรียลไทม์ หากต้องการใช้คอมโพเนนต์นี้จริงๆ คุณต้องนำเข้าลงในไฟล์ speech.js
ของคุณ:
// speech.js import React from 'react' import SSMLeditor from './SSMLeditor.js' export default { name: 'speech', type: 'array', title: 'SSML Editor', inputComponent: SSMLeditor, of: [ { type: 'block', styles: [], lists: [], marks: { decorators: [], annotations: [ { type: 'alias' }, { type: 'emphasis' }, { type: 'prosody' }, { type: 'sayAs' }, ], }, }, ], }
เมื่อคุณบันทึกสิ่งนี้และรีโหลดสตูดิโอ มันควรจะดูเหมือนกันทุกประการ แต่นั่นเป็นเพราะเรายังไม่ได้เริ่มปรับแต่งตัวแก้ไข
แปลงข้อความแบบพกพาเป็น SSML
ตัวแก้ไขจะบันทึกเนื้อหาเป็น Portable Text ซึ่งเป็นอาร์เรย์ของออบเจ็กต์ใน JSON ที่ทำให้ง่ายต่อการแปลง Rich Text เป็นรูปแบบใดก็ได้ที่คุณต้องการให้เป็น เมื่อคุณแปลงข้อความแบบพกพาเป็นไวยากรณ์หรือรูปแบบอื่น เราเรียกว่า "การทำให้เป็นอนุกรม" ดังนั้น "serializers" จึงเป็นสูตรสำหรับการแปลง Rich Text ในส่วนนี้ เราจะเพิ่ม serializers สำหรับการสังเคราะห์เสียงพูด
คุณได้สร้างไฟล์ blocksToSSML.js แล้ว ตอนนี้เราจะต้องเพิ่มการพึ่งพาครั้งแรกของเรา เริ่มต้นด้วยการรันคำสั่งเทอร์มินัล npm init -y
ภายในโฟลเดอร์ ssml-editor
สิ่งนี้จะเพิ่ม package.json ที่จะแสดงรายการการพึ่งพาของเอดิเตอร์
เมื่อเสร็จแล้ว คุณสามารถเรียกใช้ npm install @sanity/block-content-to-html
เพื่อรับไลบรารี่ที่ทำให้ Portable Text เป็นอนุกรมได้ง่ายขึ้น เรากำลังใช้ไลบรารี HTML เนื่องจาก SSML มีไวยากรณ์ XML เดียวกันกับแท็กและแอตทริบิวต์
นี่เป็นโค้ดจำนวนมาก ดังนั้นโปรดคัดลอกและวาง ฉันจะอธิบายรูปแบบด้านล่างตัวอย่าง:
// blocksToSSML.js import blocksToHTML, { h } from '@sanity/block-content-to-html' const serializers = { marks: { prosody: ({ children, mark: { rate, pitch, volume } }) => h('prosody', { attrs: { rate, pitch, volume } }, children), alias: ({ children, mark: { text } }) => h('sub', { attrs: { alias: text } }, children), sayAs: ({ children, mark: { interpretAs } }) => h('say-as', { attrs: { 'interpret-as': interpretAs } }, children), break: ({ children, mark: { time, strength } }) => h('break', { attrs: { time: '${time}ms', strength } }, children), emphasis: ({ children, mark: { level } }) => h('emphasis', { attrs: { level } }, children) } } export const blocksToSSML = blocks => blocksToHTML({ blocks, serializers })
รหัสนี้จะส่งออกฟังก์ชันที่ใช้อาร์เรย์ของบล็อกและวนซ้ำ เมื่อใดก็ตามที่บล็อกมี mark
บล็อกจะค้นหาซีเรียลไลเซอร์สำหรับประเภทนั้น หากคุณทำเครื่องหมายข้อความบางข้อความเพื่อ emphasis
จะเป็นฟังก์ชันนี้จากอ็อบเจ็กต์ serializers:
emphasis: ({ children, mark: { level } }) => h('emphasis', { attrs: { level } }, children)
บางทีคุณอาจรู้จักพารามิเตอร์ที่เรากำหนดสคีมา ฟังก์ชัน h()
ช่วยให้เรากำหนดองค์ประกอบ HTML ได้ นั่นคือ เรา "โกง" และทำให้ส่งคืนองค์ประกอบ SSML ที่เรียกว่า <emphasis>
นอกจากนี้เรายังให้ level
แอตทริบิวต์หากมีการกำหนดไว้ และวางองค์ประกอบ children
ไว้ภายใน ซึ่งโดยส่วนใหญ่แล้วจะเป็นข้อความที่คุณทำเครื่องหมายด้วย emphasis
{ "_type": "block", "_key": "f2c4cf1ab4e0", "style": "normal", "markDefs": [ { "_type": "emphasis", "_key": "99b28ed3fa58", "level": "strong" } ], "children": [ { "_type": "span", "_key": "f2c4cf1ab4e01", "text": "Say this strongly!", "marks": [ "99b28ed3fa58" ] } ] }
นั่นคือวิธีที่โครงสร้างด้านบนใน Portable Text ถูกทำให้เป็นอนุกรมกับ SSML นี้:
<emphasis level="strong">Say this strongly</emphasis>
หากคุณต้องการการสนับสนุนสำหรับแท็ก SSML เพิ่มเติม คุณสามารถเพิ่มคำอธิบายประกอบเพิ่มเติมในสคีมา และเพิ่มประเภทคำอธิบายประกอบไปยังส่วน marks
ใน serializers
ตอนนี้ เรามีฟังก์ชันที่ส่งคืนมาร์กอัป SSML จาก Rich Text ที่มาร์กอัปของเรา ส่วนสุดท้ายคือการสร้างปุ่มที่ช่วยให้เราส่งมาร์กอัปนี้ไปยังบริการแปลงข้อความเป็นคำพูด
การเพิ่มปุ่มแสดงตัวอย่างที่ตอบกลับคุณ
ตามหลักการแล้ว เราควรใช้ความสามารถในการสังเคราะห์เสียงพูดของเบราว์เซอร์ใน Web API ด้วยวิธีนี้ เราจะได้โค้ดและการพึ่งพาน้อยลง
อย่างไรก็ตาม ในช่วงต้นปี 2019 การรองรับเบราว์เซอร์เนทีฟสำหรับการสังเคราะห์เสียงพูดยังอยู่ในช่วงเริ่มต้น ดูเหมือนว่าการสนับสนุน SSML กำลังจะมา และมีการพิสูจน์แนวคิดของการนำ JavaScript ฝั่งไคลเอ็นต์ไปใช้งาน
โอกาสที่คุณจะใช้เนื้อหานี้กับผู้ช่วยเสียงอยู่แล้ว ทั้ง Google Assistant และ Amazon Echo (Alexa) รองรับ SSML เป็นคำตอบในการปฏิบัติตามข้อกำหนด ในบทช่วยสอนนี้ เราจะใช้ API การแปลงข้อความเป็นคำพูดของ Google ซึ่งฟังดูดีและรองรับหลายภาษา
เริ่มต้นด้วยการรับคีย์ API โดยลงชื่อสมัครใช้ Google Cloud Platform (จะให้บริการฟรีสำหรับ 1 ล้านอักขระแรกที่คุณดำเนินการ) เมื่อคุณลงชื่อสมัครใช้แล้ว คุณสามารถสร้างคีย์ API ใหม่ในหน้านี้
ตอนนี้คุณสามารถเปิดไฟล์ PreviewButton.js และเพิ่มโค้ดนี้เข้าไป:
// PreviewButton.js import React from 'react' import Button from 'part:@sanity/components/buttons/default' import { blocksToSSML } from './blocksToSSML' // You should be careful with sharing this key // I put it here to keep the code simple const API_KEY = '<yourAPIkey>' const GOOGLE_TEXT_TO_SPEECH_URL = 'https://texttospeech.googleapis.com/v1beta1/text:synthesize?key=' + API_KEY const speak = async blocks => { // Serialize blocks to SSML const ssml = blocksToSSML(blocks) // Prepare the Google Text-to-Speech configuration const body = JSON.stringify({ input: { ssml }, // Select the language code and voice name (AF) voice: { languageCode: 'en-US', name: 'en-US-Wavenet-A' }, // Use MP3 in order to play in browser audioConfig: { audioEncoding: 'MP3' } }) // Send the SSML string to the API const res = await fetch(GOOGLE_TEXT_TO_SPEECH_URL, { method: 'POST', body }).then(res => res.json()) // Play the returned audio with the Browser's Audo API const audio = new Audio('data:audio/wav;base64,' + res.audioContent) audio.play() } export default function PreviewButton (props) { return <Button style={{ marginTop: '1em' }} onClick={() => speak(props.blocks)}>Speak text</Button> }
ฉันได้เก็บโค้ดปุ่มแสดงตัวอย่างนี้ไว้ให้น้อยที่สุดเพื่อให้ง่ายต่อการปฏิบัติตามบทช่วยสอนนี้ แน่นอน คุณสามารถสร้างมันขึ้นมาโดยเพิ่มสถานะเพื่อแสดงว่าการแสดงตัวอย่างกำลังดำเนินการอยู่ หรือทำให้สามารถแสดงตัวอย่างด้วยเสียงต่างๆ ที่ API ของ Google รองรับได้
เพิ่มปุ่มไปที่ SSMLeditor.js
:
// SSMLeditor.js import React, { Fragment } from 'react'; import { BlockEditor } from 'part:@sanity/form-builder'; import PreviewButton from './PreviewButton'; export default function SSMLeditor(props) { return ( <Fragment> <BlockEditor {...props} /> <PreviewButton blocks={props.value} /> </Fragment> ); }
ตอนนี้ คุณควรจะสามารถทำเครื่องหมายข้อความของคุณด้วยคำอธิบายประกอบต่างๆ และได้ยินผลลัพธ์เมื่อกด "พูดข้อความ" เจ๋งใช่มั้ย
คุณได้สร้างโปรแกรมแก้ไขการสังเคราะห์เสียงแล้ว และตอนนี้คืออะไร
หากคุณได้ทำตามบทช่วยสอนนี้ แสดงว่าคุณได้ผ่านวิธีใช้เครื่องมือแก้ไขสำหรับ Portable Text ใน Sanity Studio เพื่อสร้างคำอธิบายประกอบแบบกำหนดเองและปรับแต่งตัวแก้ไขเองแล้ว คุณสามารถใช้ทักษะเหล่านี้ได้กับทุกสิ่ง ไม่เพียงแต่สร้างโปรแกรมแก้ไขการสังเคราะห์เสียงเท่านั้น คุณยังเคยผ่านวิธีทำให้ Portable Text เป็นอนุกรมในไวยากรณ์ที่คุณต้องการ เห็นได้ชัดว่าสิ่งนี้มีประโยชน์หากคุณกำลังสร้างส่วนหน้าใน React หรือ Vue คุณสามารถใช้ทักษะเหล่านี้เพื่อสร้าง Markdown จาก Portable Text ได้
เราไม่ได้กล่าวถึงวิธีที่คุณใช้งานจริงร่วมกับผู้ช่วยเสียง หากคุณต้องการลอง คุณสามารถใช้ตรรกะเดียวกันกับปุ่มแสดงตัวอย่างในฟังก์ชันแบบไร้เซิร์ฟเวอร์ และตั้งค่าเป็นปลายทาง API สำหรับการเติมเต็มโดยใช้เว็บฮุค เช่น ด้วย Dialogflow
หากคุณต้องการให้ฉันเขียนบทช่วยสอนเกี่ยวกับวิธีใช้ตัวแก้ไขการสังเคราะห์เสียงพูดกับผู้ช่วยเสียง โปรดบอกใบ้บน Twitter หรือแบ่งปันในส่วนความคิดเห็นด้านล่าง
อ่านเพิ่มเติม เกี่ยวกับ SmashingMag:
- ทดลองสังเคราะห์เสียงพูด
- ยกระดับประสบการณ์ผู้ใช้ด้วย Web Speech API
- Accessibility APIs: กุญแจสำคัญในการเข้าถึงเว็บ
- สร้าง AI Chatbot อย่างง่ายด้วย Web Speech API และ Node.js