ดูวิธีสร้างประสบการณ์การใช้งานเสียงแบบแฮนด์ฟรีที่มีประสิทธิภาพซึ่งขับเคลื่อนโดย AI ในอุปกรณ์ เราจะพูดถึงทุกสิ่งที่คุณต้องการ ไม่ว่าจะเป็นการตั้งค่ากิจกรรมของแว่นตา การบันทึกฟีดไมโครโฟนและฟีดกล้อง รวมถึงการใช้เสียงร่วมกับสัญญาณเสียงอื่นๆ เป็น UI ขั้นสุดยอดของคุณ ผ่านการสาธิตที่น่าตื่นเต้นในโลกแห่งความเป็นจริงของ XR Language Teacher แบบโอเพนซอร์สที่สร้างขึ้นในแอป Google AI Edge Gallery คุณจะได้เรียนรู้วิธีจัดการ Gemini Nano 4 (สร้างขึ้นบน Gemma 4 และเพิ่มประสิทธิภาพสำหรับอุปกรณ์ Android) และ ML Kit รวมถึงใช้ประโยชน์จากรันไทม์ LiteRT เพื่อสร้างประสบการณ์ XR ที่รวดเร็วซึ่งสามารถฟัง เห็น และพูดได้อย่างเป็นธรรมชาติ
ข้อความถอดเสียง
สวัสดีและขอต้อนรับทุกคน
ดีใจมากที่ได้เจอทุกคนในวันนี้
ขอบคุณที่มา
ฉันชื่อ Amy
วันนี้ Alice เพื่อนร่วมงานของฉันจะมาร่วมพูดคุยเกี่ยวกับวิธีสร้างประสบการณ์ที่เน้นเสียงเป็นหลักอันน่าทึ่งสำหรับแว่นตาอัจฉริยะโดยใช้ Gemini Nano 4
มาเริ่มกันเลย
ดังนั้นเมื่อพูดถึงแว่นตาอัจฉริยะ เราหมายถึงแว่นตาแบบพกพาที่มีน้ำหนักเบาซึ่งออกแบบมาให้สวมใส่ได้ตลอดทั้งวัน
หมวดหมู่นี้จะรวมทั้งแว่นตาที่มีจอแสดงผลและแว่นตาที่มีเฉพาะเสียง และคุณจะเห็นฮาร์ดแวร์ที่หลากหลายในอุปกรณ์เหล่านี้
สิ่งแรกที่คุณจะสังเกตเห็นคือแว่นตาจะมีทัชแพดซึ่งใช้สำหรับการป้อนข้อมูลของผู้ใช้ เช่น การแตะและการปัด
นอกจากนี้ยังมีลำโพง กล้อง และไมโครโฟนด้วย
และอาจมีจอแสดงผลหรือไม่มีก็ได้ หรืออาจมีทั้ง 2 แบบผสมกัน
วันนี้เราจะพูดถึงการเน้นเสียงเป็นหลักและวิธีสร้างประสบการณ์การใช้งานที่น่าสนใจ ชาญฉลาด และใช้งานได้ทุกที่ทุกเวลา
และสิ่งหนึ่งที่น่าสนใจเกี่ยวกับเสียงเท่านั้นก็คือเสียงจะกลายเป็น UI ของคุณที่นี่
และ AI ในอุปกรณ์เหมาะอย่างยิ่งสำหรับนักพัฒนาแอปที่ต้องการประสบการณ์การใช้งานแบบออฟไลน์และขณะเดินทาง
และเรามีข่าวดีมาบอก
ดังนั้นหากคุณสร้างแอปสำหรับ Android อยู่แล้ว ก็เท่ากับว่าคุณสร้างแอปสำหรับ Android XR อยู่แล้ว
แอป Android ที่มีอยู่ได้รับการออกแบบมาให้ทำงานร่วมกับแว่นตาที่มีระบบเสียงได้อย่างมีประสิทธิภาพ โดยจะส่งการอ่านออกเสียงการแจ้งเตือนและเสียงอื่นๆ ผ่านบลูทูธ
และหากต้องการก้าวไปอีกขั้นและสร้างความแตกต่างให้แอปสำหรับ XR ก็มีวิธีมากมายให้เลือก
ดังนั้นในการพูดคุยครั้งนี้ เราจะพูดถึงโมเดลในอุปกรณ์ แต่ก็ไม่ได้หมายความว่าจะมีเพียงเท่านี้
เรายังมีโมเดลระบบคลาวด์ด้วย
มีสิ่งต่างๆ เช่น Gemini Live API
คุณสามารถผสานรวมกับ Agent ของระบบ เช่น Gemini และทั้งหมดนี้จะช่วยคุณสร้างประสบการณ์การใช้งานที่ดีที่สุดสำหรับคุณได้
แล้วคุณจะสร้างประสบการณ์ที่ได้ยิน มองเห็น และพูดได้อย่างเป็นธรรมชาติได้อย่างไร
วันนี้เราจะแนะนำขั้นตอนการสร้างประสบการณ์การใช้งานแบบแฮนด์ฟรีที่มีประสิทธิภาพซึ่งทำงานด้วยระบบ AI ในอุปกรณ์
ผมจะแนะนำข้อมูลพื้นฐานให้คุณทราบ ตั้งแต่วิธีสร้างแอปสำหรับแว่นตา วิธีตั้งค่ากิจกรรม ขอสิทธิ์ที่จำเป็น และวิธีใช้ ML Kit สำหรับ UI แบบสนทนา
จากนั้นฉันจะส่งต่อให้ Alice ซึ่งจะพูดถึงตัวอย่างจริงที่ใช้ Gemini Nano 4
มาดูโค้ดกันเลย
ดังนั้นเมื่อใช้แว่นตา โทรศัพท์จะเป็นอุปกรณ์ที่ประมวลผลอย่างหนัก และเนื้อหาในแว่นตาจะฉายจากโทรศัพท์ไปยังแว่นตา
ดังนั้นคุณจึงต้องมี 2 สิ่งนี้เพื่อเชื่อมต่ออุปกรณ์เหล่านี้เมื่อตั้งค่าเป็นครั้งแรก
สิ่งแรกที่คุณต้องมีคือไลบรารี Jetpack Projected
คุณจะต้องเพิ่มทรัพยากร Dependency นี้ ซึ่งเป็นสิ่งที่จะมี API เพื่อขับเคลื่อนประสบการณ์การใช้งานที่คาดการณ์ไว้
dependencies {
implementation("androidx.xr.projected:projected:1.0.0-alpha13")
}และคุณจะต้องสร้างกิจกรรมใหม่สำหรับแว่นตาโดยเฉพาะด้วย
class ProjectedMainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// Glasses content
}
}
}จากนั้นคุณจะใช้ประโยชน์จากไลบรารี Jetpack Projected ทั้งภายในแอปที่มีอยู่และภายในกิจกรรมใหม่นี้ เพื่อให้อุปกรณ์ทั้ง 2 เครื่องสื่อสารกันได้อย่างราบรื่นทุกเมื่อที่ต้องการ
ดังนั้น คุณจะต้องประกาศกิจกรรมนี้ในไฟล์ Manifest เช่นเดียวกับกิจกรรมปกติอื่นๆ ที่คุณเคยเห็น
<activity
android:name=".ProjectedMainActivity"
android:exported="false"
android:requiredDisplayCategory="android.hardware.display.category.XR_PROJECTED"
android:label="Example activity for audio glasses">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.XR_PROJECTED_LAUNCHER"/>
</intent-filter>
</activity>ความแตกต่างหลักคือคุณต้องใช้ XR_PROJECTED เป็น requiredDisplayCategory
เพื่อให้ระบบทราบว่ากิจกรรมนี้มีไว้สำหรับแว่นตา
และคุณยังต้องใช้หมวดหมู่ความตั้งใจนี้ด้วย ซึ่งก็คือ XR_PROJECTED_LAUNCHER
ซึ่งจะช่วยให้ Gemini ค้นพบแอปของคุณเพื่อให้ผู้ใช้เปิดแอปแบบแฮนด์ฟรีได้ทุกที่ทุกเวลา
โอเค
ดังนั้นเมื่อตั้งค่ากิจกรรมนั้นแล้ว อย่างที่ฉันได้กล่าวไป คุณจะใช้ไลบรารีที่คาดการณ์ไว้เพื่อขับเคลื่อนประสบการณ์การใช้งานแอปนั้น
ไลบรารีนี้มีเครื่องมือทั้งหมดที่จำเป็นสำหรับการสร้างประสบการณ์การใช้งานแว่นตาอย่างเต็มรูปแบบ
เรามาดู API หลักๆ บางส่วนที่คุณจะใช้ที่นี่กัน
วิธีแรกที่อยากพูดถึงคือวิธี isProjectedDeviceConnected
// Continuously monitor whether the glasses are connected
val isGlassesConnected by ProjectedContext.isProjectedDeviceConnected(
context,
scope.coroutineContext
).collectAsStateWithLifecycle(initialValue = false)
// Launch the activity
val options = ProjectedContext.createProjectedActivityOptions(context)
val intent = Intent(context, ProjectedMainActivity::class.java)
context.startActivity(intent, options.toBundle())
// Check if the device is audio only or has a display
val projectedDeviceController = ProjectedDeviceController.create(this@ProjectedMainActivity)
isVisualUiSupported = projectedDeviceController.capabilities.contains(CAPABILITY_VISUAL_UI)วิธีนี้จะช่วยให้คุณตรวจสอบการเชื่อมต่อแว่นตาได้อย่างต่อเนื่อง
คุณใช้ข้อมูลนี้ร่วมกับวงจรของอุปกรณ์เพื่อดูว่าแว่นตาพร้อมใช้งานจริงเมื่อใด ไม่ใช่แค่เมื่อจับคู่หรือเปิดเครื่อง แต่ยังรวมถึงเมื่อสวมใส่และพร้อมใช้งานด้วย
และนี่คือสิ่งที่เกิดขึ้น
ดังนั้นข้อมูลนี้จะช่วยให้คุณทราบได้อย่างแน่ชัดว่าเมื่อใดที่คุณพร้อมที่จะเปิดตัวกิจกรรมนั้น
ดังนั้นเมื่อพร้อมที่จะเปิดตัวกิจกรรม คุณจะต้องตั้งค่าตัวเลือกเฉพาะบางอย่างเมื่อเริ่มกิจกรรม
และสำหรับคำถามเหล่านั้น คุณจะใช้ createProjectedActivityOptions ใน ProjectedContext
และขอย้ำอีกครั้งว่าคุณจะใช้ข้อมูลนั้นกับบริบทของความตั้งใจนั้น
และอีกเครื่องมือหนึ่งที่คุณควรใช้คือProjectedDeviceController
วิธีตรวจสอบประเภทแว่นตาที่ผู้ใช้สวมอยู่มีดังนี้
ดังที่กล่าวไปก่อนหน้านี้ว่าแว่นตาอัจฉริยะมีทั้งแว่นตาแบบแสดงผลและแบบเสียงเท่านั้น คุณจะต้องดูว่าความสามารถของแว่นตามี CAPABILITY_VISUAL_UI นี้หรือไม่ ซึ่งจะช่วยให้คุณปรับแอปให้เข้ากับอุปกรณ์ต่างๆ ได้อย่างราบรื่นและปรับแต่งประสบการณ์การใช้งานที่เหมาะกับอุปกรณ์นั้นๆ
ดังนั้นในขณะที่สร้างประสบการณ์ที่แตกต่างนี้ คุณอาจต้องการเข้าถึงฮาร์ดแวร์ของอุปกรณ์ เช่น การใช้ประโยชน์จากกล้องของแว่นตา
ดังนั้น หากต้องการดำเนินการนี้จากกิจกรรมที่ฉายภาพ คุณจะต้องขอสิทธิ์ที่เหมาะสมโดยใช้ ProjectedActivityCompat
// In a projected activity
private fun requestProjectedPermissions() {
lifecycleScope.launch {
try {
ProjectedActivityCompat.requestPermissions(
this@PermissionsFromProjectedActivity,
arrayOf(Manifest.permission.CAMERA),
// CAMERA_PERMISSION_REQUEST_CODE is a developer defined constant
requestCode = CAMERA_PERMISSION_REQUEST_CODE,
)
} catch (e: IllegalStateException) {
// Thrown when the projected system service can't be bound.
Log.e(TAG, "Request failed: projected service unavailable.", e)
}
}
}และแม้ว่าแอปของคุณจะมีสิทธิ์ที่จำเป็นสำหรับโทรศัพท์อยู่แล้ว คุณก็ยังต้องขอสิทธิ์เฉพาะสำหรับฮาร์ดแวร์ของแว่นตาด้วย
และเมื่อใดก็ตามที่ทำงานกับสิทธิ์ โปรดคำนึงถึงแนวทางปฏิบัติแนะนำสำหรับ Android โดยทั่วไป
ประกาศสิทธิ์ที่จำเป็นในไฟล์ Manifest ขอสิทธิ์ภายในบริบทที่จำเป็น และขอเฉพาะสิทธิ์ที่คุณต้องการจริงๆ เพื่อให้แอปทำงานได้
แน่นอนว่าเราได้จัดทำเอกสารเกี่ยวกับเรื่องนี้ไว้ในเว็บไซต์ developer.android.com
แต่เรายังมีทักษะใหม่ล่าสุดด้วย นั่นคือทักษะฮาร์ดแวร์ที่คาดการณ์ไว้
คุณสามารถใช้ทักษะนี้กับ Android CLI ใน Android Studio เพื่อตั้งค่าเวิร์กโฟลว์สิทธิ์ และคุณจะเข้าถึงทักษะนี้ รวมถึงทักษะอื่นๆ ทั้งหมดของ Android ได้ด้วย
android skills add projected-hardware
android skills add --allเมื่อเราเชื่อมต่อแล้ว ทราบถึงความสามารถของฮาร์ดแวร์ และมีสิทธิ์ที่เหมาะสมแล้ว เรามาพูดถึงเครื่องมือที่คุณมีเพื่อสร้างประสบการณ์ด้านเสียงกัน
ดังนั้นเมื่อออกแบบประสบการณ์การใช้งานที่เน้นเสียงเป็นหลัก เราจึงต้องการดูแลให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่มีคุณภาพสูง
ซึ่งอาจรวมถึงการโต้ตอบที่สำคัญบางอย่าง
อย่างแรกคือการใช้เสียงเป็นเอาต์พุต ซึ่งช่วยให้เสียงของคุณพูดกับผู้ใช้อย่างเป็นธรรมชาติ
ถัดไปคือการใช้เอียร์คอนเพื่อส่งสัญญาณเสียงที่เฉพาะเจาะจงไปยังผู้ใช้
และยังจัดการอินพุตจากผู้ใช้ด้วย ไม่ว่าจะเป็นการแตะและปัดผ่านทัชแพด หรือหากคุณต้องการอนุญาตให้ผู้ใช้พูดกับแอปของคุณ
สิ่งแรกที่เราจะพูดถึงคือการใช้เสียงเป็นเอาต์พุต
ดังนั้นสำหรับเรื่องนี้ ผมขอแนะนำไลบรารี GenAI Text To Speech ใหม่ของ ML Kit
ตอนนี้ฟีเจอร์นี้เผยแพร่แล้วในเวอร์ชันอัลฟ่าในอุปกรณ์ Pixel และ Samsung บางรุ่น
เราตื่นเต้นกับเรื่องนี้มาก โดยเฉพาะอย่างยิ่งสำหรับประสบการณ์การใช้งานแบบเน้นเสียงเป็นหลักเหล่านี้
หากเคยใช้การอ่านออกเสียงข้อความมาก่อน คุณอาจทราบว่าปัญหาหนึ่งคือเสียงของระบบอาจไม่เป็นธรรมชาติอย่างที่คุณต้องการเสมอไป
และการเชื่อมต่อกับโมเดลระบบคลาวด์สำหรับเสียงอื่นๆ อาจทำให้เกิดเวลาในการตอบสนองหรือค่าใช้จ่ายเพิ่มเติม
ดังนั้น ML Kit จึงให้เสียงพูดที่ฟังดูเป็นธรรมชาติมากกว่า TTS ของระบบ
และ ML Kit มีเสียงที่เป็นเอกลักษณ์ 8 เสียงให้คุณเลือก ซึ่งมีลักษณะแตกต่างกันเพื่อตอบสนองความต้องการของแอป
นอกจากนี้ ML Kit ยังรองรับภาษาต่างๆ มากมาย โดยมีภาษาให้เลือกถึง 21 ภาษา
และยิ่งไปกว่านั้น ข้อดีอย่างหนึ่งที่สำคัญคือหลังจากดาวน์โหลดครั้งแรกแล้ว ฟีเจอร์นี้จะทำงานบนอุปกรณ์โดยสมบูรณ์
ระบบจะดาวน์โหลดโมเดลนี้ลงในระบบและแชร์ในแอปต่างๆ ดังนั้นจึงไม่เพิ่มขนาด APK ของคุณ
และไม่เพียงแต่จะใช้งานแบบออฟไลน์ได้ ซึ่งสำคัญอย่างยิ่งสำหรับผู้ใช้แว่นตาที่กำลังเดินทาง ซึ่งอาจไม่มี Wi-Fi หรืออาจอยู่ในพื้นที่ที่มีสัญญาณไม่ดี แต่คุณก็มีแนวโน้มที่จะเห็นประสิทธิภาพที่ดีขึ้นด้วย เนื่องจากไม่ต้องทำการเรียกเซิร์ฟเวอร์ไปมา
โอเค
เรามาดูรายละเอียดของไลบรารีและวิธีสร้างด้วยไลบรารีกัน
ก่อนอื่น คุณจะต้องเพิ่มทรัพยากร Dependency
dependencies {
// ML Kit GenAI TTS SDK
implementation("com.google.mlkit:genai-text-to-speech:1.0.0-alpha1")
}ดังนั้นในกรณีนี้ คุณต้องใช้เพียง SDK การอ่านออกเสียงข้อความของ ML Kit เท่านั้น
จากนั้นคุณก็เริ่มเขียนโค้ดได้เลย
ดังนั้น ไลบรารี GenAI ML Kit จึงมี Speech Synthesizer API ด้วย
// 1. Initializing the SpeechSynthesizer client.
val options = SpeechSynthesizerOptions.Builder(Locale.US).build()
val client = SpeechSynthesis.getClient(options)
// 2. Check and download the voice model.
val voiceInfo = client.getSupportedVoices().firstOrNull()
if (voiceInfo != null && client.checkStatus(voiceInfo) == FeatureStatus.DOWNLOADABLE) {
client.download(voiceInfo).collect { status -> println("Download status: $status") }
} else if (voiceInfo == null) {
// Tip: Fallback to system TTS if ML Kit voice is unavailable
}ซึ่งจะช่วยให้คุณใช้เสียงที่เลือกได้อย่างตรงไปตรงมา
และขั้นตอนแรกที่คุณจะทำที่นี่คือการเริ่มต้นไคลเอ็นต์โดยใช้ Speech Synth- Synthesizer อีกครั้ง โดยตั้งค่าภาษาที่คุณเลือก
จากนั้นให้ตรวจสอบสถานะการดาวน์โหลดและดูว่ามีเสียงหรือไม่ แล้วเลือกเสียง
ในตัวอย่างนี้ เราจะเลือกเสียงแรก แต่เราก็อยากจะบอกด้วยว่าเรากำลังใช้ firstOrNull
อย่างที่บอกไปว่า ML Kit ต้องใช้โทรศัพท์ที่มีความสามารถเฉพาะ ซึ่งก็คืออุปกรณ์ Samsung หรือ Pixel
ดังนั้น หากคุณได้รับค่า Null อาจหมายความว่าผู้ใช้อาจไม่ได้ใช้โทรศัพท์ที่เข้ากันได้
จากนั้นคุณต้องตรวจสอบว่ามีฟังก์ชันสำรองไว้ ไม่ว่าจะเป็นการกลับไปใช้การอ่านออกเสียงข้อความของระบบ หรือการเล่นข้อความที่บันทึกไว้ หรืออะไรทำนองนั้น
โอเค จากนั้นคุณจะใช้การสังเคราะห์เสียงพูด SpeechSynthesizerRequest เพื่อสร้างคำขอ โดยป้อนข้อความที่ต้องการ จากนั้นตั้งค่าการใช้งานและประเภทเนื้อหา
// 3. Construct the SpeechSynthesizerRequest
val request = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Text("Hello world", voiceInfo),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build()
)
// 4. Speak and collect status
client.synthesize(request).collect { response ->
when (response) {
is SpeechSynthesizerResponse.StatusUpdate -> println("Playback status: ${response.status}")
}
}เล่นเสียงพูด ตรวจสอบสถานะ และรับการแจ้งเตือนเพื่อให้คุณทราบเมื่อเสียงพูดจบลง จากนั้นรวบรวมข้อผิดพลาดที่อาจเกิดขึ้น
จากนั้นปิดทรัพยากรเมื่อจำเป็น
// 4. Speak and collect status
client.synthesize(request).collect { response ->
when (response) {
is SpeechSynthesizerResponse.StatusUpdate -> println("Playback status: ${response.status}")
}
}
// 5. Release resources when finished (e.g., on Activity destroy)
override fun onDestroy() {
super.onDestroy()
client.close()
}ซึ่งก็ค่อนข้างตรงไปตรงมาใช่ไหม
ขั้นตอนเหล่านี้จะช่วยเปลี่ยนเสียงของแอปจากเสียงแบบนี้…
สวัสดี นักพัฒนาแอป Android
ดีใจมากที่ได้เจอทุกคนในวันนี้
เราหวังว่าคุณจะเพลิดเพลินกับการนำเสนอของเราเกี่ยวกับประสบการณ์การใช้งานที่เน้นเสียงเป็นหลัก
เป็นเสียงที่ฟังดูเหมือนแบบนี้มากขึ้น
สวัสดี นักพัฒนาแอป Android
ดีใจมากที่ได้เจอทุกคนในวันนี้
เราหวังว่าคุณจะเพลิดเพลินกับการนำเสนอของเราเกี่ยวกับประสบการณ์การใช้งานที่เน้นเสียงเป็นหลัก
ดีขึ้นมากเลยใช่ไหม
ตกลง
ไลบรารีการอ่านออกเสียงข้อความของ ML Kit เป็นเครื่องมือที่มีประสิทธิภาพมาก แต่คุณไม่ได้จำกัดอยู่เพียงการใช้เสียงเหล่านี้เท่านั้น คุณยังเล่นเอียร์คอนด้วยไลบรารีนี้ได้ด้วย
// 1. Register Earcon resource with Builder
SpeechSynthesizerOptions.Builder(Locale.US)
.addEarcon(
name = "shutter_sound",
Uri = "path/to/earcon.file".toUri()
)
.build()
// 2. Request Earcon playback
val request = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Earcon(
name = "shutter_sound"
),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes
.USAGE_ASSISTANCE_SONIFICATION)
.setContentType(AudioAttributes
.CONTENT_TYPE_SONIFICATION)
.build()
)ดังนั้น เอียร์คอนตามชื่อก็คือไอคอนเสียงนั่นเอง
ซึ่งจะช่วยให้คุณเล่นเสียงสั้นๆ เพื่อสื่อสารกับผู้ใช้ได้
และเช่นเดียวกับไอคอนภาพที่มีเอียร์คอน ผู้ใช้จะต้องรู้ว่าสิ่งเหล่านี้คืออะไรและหมายถึงอะไร
ดังนั้น ในช่วงแรกคุณอาจต้องจับคู่การแจ้งเตือนแบบภาพกับการแจ้งเตือนด้วยเสียงเพื่อให้ชัดเจน
และเราได้สร้างเสียงมาตรฐานบางอย่างที่พร้อมใช้งานใน Android Design Hub แล้ว คุณสามารถใช้เสียงเหล่านั้นได้ โดยดาวน์โหลดจากที่นั่นโดยตรงและใช้เสียงเหล่านั้นสำหรับสิ่งต่างๆ เช่น การเปิดแอป การปิดแอป การแจ้งเตือนข้อผิดพลาด และการปิดเสียงหรือเปิดเสียง
นอกจากนี้ คุณยังมีตัวเลือกในการสร้างเอียร์คอนที่กำหนดเองได้ด้วย
ดังนั้น เราขอแนะนำให้คุณทำตามแนวทางปฏิบัติแนะนำบางอย่างเกี่ยวกับเอียร์คอน ไม่ว่าคุณจะใช้เอียร์คอนที่ดาวน์โหลดจาก Design Hub หรือสร้างขึ้นเองก็ตาม
ดังนั้น คุณจึงควรพิจารณาอย่างรอบคอบเกี่ยวกับวิธีใช้เอียร์คอนและวิธีใช้
ดังนั้นจึงมีบางสิ่งที่ควรคำนึงถึง เช่น สำหรับผู้เริ่มต้น ให้พยายามอย่าใช้เอียร์คอนมากเกินไป
อย่าเล่นเสียงเดิมซ้ำไปซ้ำมา เพราะอาจทำให้เกิดอาการที่เรียกว่า "หูล้า"
และอย่าลืมว่าเอียร์คอนควรเป็นเสียงที่ยอดเยี่ยมและเข้าใจง่าย รวมถึงใช้ระดับเสียงและจังหวะที่คุ้นเคยเมื่อนำไปใช้
และพยายามหลีกเลี่ยงเอียร์คอนที่ซับซ้อนเกินไปซึ่งต้องมีคำอธิบาย เพราะอาจเหมาะกับการแจ้งเตือนด้วยเสียงมากกว่า
และโปรดทราบว่าเสียงที่อาจฟังดูเบาในสตูดิโอหรือในระบบของคุณ หรือในลำโพงคอมพิวเตอร์ อาจกลายเป็นเสียงที่น่ารำคาญหลังจากที่ผู้ใช้เล่นซ้ำๆ
ดังนั้นพยายามอย่าส่งเสียงแจ้งเตือนผู้ใช้มากเกินไปจนทำให้สับสนหรือทำให้ผู้ใช้เพิกเฉยต่อการแจ้งเตือนที่สำคัญ
โอเค
ตอนนี้เราจึงสามารถให้สัญญาณเสียงและการตอบกลับได้ ไม่ว่าจะผ่านคำพูดที่เป็นธรรมชาติหรือการใช้เอียร์คอน
แล้วข้อมูลที่ป้อนเข้าไปล่ะ
แน่นอนว่ามีทัชแพดหรือปุ่มเฉพาะบนอุปกรณ์ด้วย ซึ่งคุณสามารถใช้เพื่อสร้างการโต้ตอบด้วยการแตะและปัดได้
แต่คุณอาจต้องการอนุญาตให้ใช้ UI แบบสนทนานี้ด้วย ซึ่งคุณสามารถใช้ GenAI Speech Recognition API ของ ML Kit ได้
ปัจจุบันฟีเจอร์นี้อยู่ในเวอร์ชันอัลฟ่าและพร้อมใช้งานในอุปกรณ์บางรุ่น แต่ฟีเจอร์พื้นฐานนี้จะทำงานใน 2 โหมดที่แตกต่างกัน
โหมดพื้นฐานจะใช้การจดจำคำพูดในอุปกรณ์แบบดั้งเดิม และพร้อมใช้งานในอุปกรณ์ Android ที่มีระดับ API 31 ขึ้นไป
นอกจากนี้ยังมีโหมดขั้นสูงที่ใช้ Gemini Nano ซึ่งพร้อมให้บริการในอุปกรณ์ Pixel 10 ขึ้นไป โดยโหมดนี้จะช่วยให้คุณเห็นคุณภาพที่ดีขึ้นและครอบคลุมภาษาได้มากขึ้นด้วย
ดังนั้น SpeechRecognizer จึงใช้รูปแบบโค้ดที่คล้ายกับ SpeechSynthesizer มาก
คุณสร้างไคลเอ็นต์ ตรวจสอบว่าโมเดลพร้อมใช้งานหรือไม่ ดาวน์โหลดหากจำเป็น สร้างคำขอ และปล่อยทรัพยากร
ดังนั้นเราจะเน้นที่ความแตกต่างหลักๆ ในการสร้างคำขอ การบันทึกเสียงจากไมโครโฟน และการประมวลผลผลลัพธ์
และเช่นเคย คุณจะต้องเพิ่มทรัพยากร Dependency ที่จำเป็น
dependencies {
// ML Kit Speech Recognition
implementation("com.google.mlkit:genai-speech-recognition:1.0.0-alpha1")
}สิ่งที่คุณต้องใช้ที่นี่คือ Speech Recognition API
จากนั้นคุณจะเริ่มสร้างคำขอได้
ดังนั้นคุณจะใช้ฟังก์ชันระงับเมื่อกำหนดตรรกะการจดจำ
suspend fun startMyRecognition() {
// choose locale and mode
val options: SpeechRecognizerOptions =
speechRecognizerOptions {
locale = Locale.US
preferredMode = SpeechRecognizerOptions.Mode.MODE_ADVANCED
}
val speechRecognizer: SpeechRecognizer = SpeechRecognition.getClient(options)
val request: SpeechRecognizerRequest =
speechRecognizerRequest { audioSource = AudioSource.fromMic() }
...จากนั้นระบุตัวเลือกการจดจำเสียงพูด โดยตั้งค่าภาษาและ preferredMode
และคุณยังเลือก audioSource และกำหนดคำขอได้ที่นี่ด้วย
จากนั้นคุณจะเริ่มการจดจำและรวบรวมคำตอบ
speechRecognizer.startRecognition(request).collect { response ->
when (response) {
is SpeechRecognizerResponse.PartialTextResponse -> {
// Real-time: Act immediately if trigger phrase is heard
handleResponse(response.text, speechRecognizer)
}
is SpeechRecognizerResponse.FinalTextResponse -> {
// Final result and recognition
handleResponse(response.text, speechRecognizer)
}
is SpeechRecognizerResponse.CompletedResponse -> {
resetListeningUi()
}
is SpeechRecognizerResponse.ErrorResponse -> {
Log.e("SpeechRec", "Error: ${response.e.errorCode}", response.e)
resetListeningUi()
}
...ดังนั้นเมื่อใช้ SpeechRecognizer คุณจะได้รับการตอบกลับ 4 รูปแบบ ได้แก่ บางส่วน, สุดท้าย, ข้อผิดพลาด และเสร็จสมบูรณ์
ส่วนการถอดเสียงบางส่วนจะดูที่สตรีมแบบเรียลไทม์ ซึ่งใช้สำหรับสิ่งต่างๆ เช่น คำสั่งเสียงหรือคีย์เวิร์ด
"Final" จะรับสตรีมเสียงสุดท้ายหลังจากวิเคราะห์และอัปเดตอย่างเต็มรูปแบบแล้ว "Completed" จะเกิดขึ้นหลังจากที่คำพูดเสร็จสมบูรณ์ และ "Error" จะรวบรวมข้อผิดพลาดที่อาจเกิดขึ้น
และนี่คือวิธีที่เรานำทุกอย่างมารวมกัน
// Helper function to check for multiple phrases and trigger the right action
private suspend fun handleResponse(text: String, recognizer: SpeechRecognizer) {
val savePhrases = listOf("save", "bookmark", "remember")
if (savePhrases.any { text.contains(it) }) {
// 1. Update app state immediately
saveItemForLater()
// 2. Stop the recognizer to enforce the action and prevent TTS echo
recognizer.stopRecognition()
// 3. Play the mute earcon for immediate, non-verbal confirmation
playEarcon()
// 4. Provide voice feedback
playAudioFeedback("Saved")
}
}สมมติว่าเราต้องการให้ผู้ใช้สามารถใช้คำสั่งเสียงเพื่อบอกให้แอปบันทึกเนื้อหาบางประเภทไว้ดูภายหลังได้
เราอาจใช้คำสั่งเสียงที่ชัดเจนเพียงคำสั่งเดียวที่เราบอกให้ผู้ใช้ใช้ หรือคุณอาจอนุญาตให้ผู้ใช้ใช้คีย์เวิร์ดและวลีจำนวนหนึ่งก็ได้
ดังนั้นเราจึงตั้งค่าให้ผู้ใช้ใช้คำว่า "บันทึก" "บุ๊กมาร์ก" หรือ "จดจำ" ได้
ทันทีที่ SpeechRecognizer ตรวจพบว่าผู้ใช้ใช้วลีใดวลีหนึ่งเหล่านั้น ระบบจะหยุดบันทึกเนื้อหาและหยุดการจดจำคำพูด เล่นเอียร์คอนที่กำหนดเอง และใช้ GenAI Text to Speech ของ ML Kit เพื่อบอกผู้ใช้ว่าบันทึกเนื้อหาแล้ว
ตกลง
เราได้พูดถึงหลายเรื่องเลย
ซึ่งอาจฟังดูเป็นนามธรรมไปหน่อย
หากคุณเป็นเหมือนฉัน คุณคงอยากเห็นตัวอย่างในชีวิตจริง
และ Alice กับผมก็อยากจะแนะนำคุณเกี่ยวกับเรื่องนี้
เธอจะแนะนำคุณเกี่ยวกับครูสอนภาษา XR ในแอป Google AI Edge Gallery
ทีมของ Alice เป็นผู้สร้างฟีเจอร์นี้ และเธอจะแสดงให้คุณเห็นวิธีการทำงานของฟีเจอร์นี้
ขอเชิญคุณได้เลย
ขอบคุณ Amy
ใช่
ฉันจึงอยากเริ่มด้วยสิ่งที่หลายคนน่าจะรู้จัก
ฉันกำลังเรียนภาษาญี่ปุ่น และใช้เวลาหลายชั่วโมงในแอปเรียนภาษา บัตรคำศัพท์ การรักษา Streak และอื่นๆ อีกมากมาย
แล้วพอฉันไปถึงโตเกียว ฉันเดินเข้าไปในตลาดและพบว่าแทบไม่มีอะไรตรงหน้าเลยที่เป็นคำศัพท์ที่ฉันเรียนมา
มีของเล่นไม้ที่เรียกว่าเคนดามะวางอยู่บนแผงขายของในตลาดซึ่งฉันจำได้เมื่อเห็น แต่ไม่รู้ว่าจะพูดเป็นภาษาญี่ปุ่นอย่างไร
มีเครื่องประดับชิ้นหนึ่งในตู้โชว์ของร้านค้าที่ฉันอยากซื้อแต่ไม่รู้ว่าเรียกว่าอะไร
นั่นคือช่วงเวลาที่ฉันมีแรงจูงใจในการเรียนรู้คำศัพท์มากที่สุด
ไม่ใช่การนั่งอยู่บนโซฟาแล้วดูแอปภาษา
ในตอนนั้น ฉันกำลังมองหาสิ่งที่ฉันอยากเรียนรู้
นี่คือสิ่งที่ฉันต้องการ
ฉันอยากใส่แว่นตาอัจฉริยะทั้งวัน แค่เดินไปรอบๆ
และเมื่อใดก็ตามที่มีอะไรสะดุดตา ก็แค่ถามออกมาดังๆ
และในตอนกลางคืนเมื่อกลับไปที่โรงแรม ทุกอย่างที่ผมถามไว้ก็จะรอผมอยู่เหมือนบัตรคำศัพท์ในโทรศัพท์
และนี่คือสิ่งที่เราสร้างขึ้น
มาดูคลิปสั้นๆ กัน
เราจึงเรียกสิ่งนี้ว่าครูสอนภาษา XR
สวัสดี
วันนี้คุณต้องการเรียนรู้เกี่ยวกับอะไร
และคุณสามารถปรับแต่งประสบการณ์การเรียนรู้ได้ในการตั้งค่าสำหรับครู
เช่น เลือกภาษาที่ต้องการเรียนรู้และเลือกตัวละครครูที่เหมาะกับคุณ
อ๋อ ใช่
และคุณสามารถสลับระหว่างโหมดแว่นตากับโหมดโทรศัพท์ได้
ฉันจะพูดว่าองุ่นเหล่านี้เป็นภาษาสเปนได้ยังไง
ดูจากภาพนี้ องุ่นแสนอร่อยเหล่านี้เรียกว่า uvas
ในภาษาสเปน คุณสามารถพูดว่า "Estas uvas son deliciosas" เพื่ออธิบายองุ่นเหล่านั้น
ฉันอยากจะบอกว่าองุ่นเขียวโดยเฉพาะ
ระบบจะทำการตรวจสอบ
หากต้องการระบุองุ่นเขียว คุณสามารถพูดว่า "Uvas verdes"
Me gustan las uvas verdes" เป็นวิธีที่ยอดเยี่ยมในการบอกใครสักคนว่าคุณชอบองุ่นเขียว
คุณจะพูดคำนี้เป็นภาษาฝรั่งเศสว่าอย่างไร และฉันจะขอ 3 อันได้อย่างไร
มาดูกันเลย
ขนมอบหน้าตาน่ารับประทานเหล่านี้เรียกว่ามาเดอลีน
ในภาษาฝรั่งเศส หากต้องการขอ 3 ชิ้น คุณสามารถพูดว่า "Je voudrais trois madeleines, s'il vous plaît."
ฉันจะขอเปลี่ยนแปลงข้อมูลนี้ได้อย่างไร
มาดูกันว่าเรามีอะไรบ้าง
หากต้องการขอเงินทอน คุณสามารถพูดว่า "Otsuri wa arimasu ka?"
ซึ่งหมายความว่า "คุณมีเงินทอนไหม"
คุณยังสามารถใช้คำว่า "Otsuri o onegaishimasu" เพื่อขอเงินทอนอย่างสุภาพได้ด้วย
จากนั้น ระบบจะบันทึกทุกสิ่งที่คุณเรียนรู้เป็นบัตรคำศัพท์ให้คุณโดยอัตโนมัติ
La botella de agua.
Esta es una botella de agua.
ใช่ และคุณสามารถทดสอบความจำและฝึกฝนได้ในแท็บการศึกษา
โอเค
ใช่
ดังนั้น ทุกอย่างที่คุณเพิ่งเห็นจึงทำงานบนอุปกรณ์ โดยไม่มีเครือข่ายในลูปเลย
ฉันเดินทางโดยใช้แพ็กเกจอินเทอร์เน็ตระหว่างประเทศ ซึ่งปกติแล้วก็ใช้งานได้ดีจนกระทั่งฉันเข้าไปในตลาดหรืออาจจะอยู่ใต้ดินหรือหลังคอนกรีตหนาๆ
ช่วงเวลาที่คุณต้องการใช้ฟีเจอร์นี้มากที่สุดมักเป็นช่วงเวลาที่สัญญาณแย่ที่สุด
และฉันอยากจะบอกว่า ประสบการณ์ที่หายไปเมื่อสัญญาณขาดหายไม่ใช่เพื่อนร่วมเดินทางที่แท้จริง เราจึงสร้าง XR Language Teacher ให้ทำงานแบบออฟไลน์และในอุปกรณ์ได้อย่างสมบูรณ์
แล้วเราสร้างเทคโนโลยีนี้ขึ้นมาอย่างไร
ในด้านสถาปัตยกรรม Amy ได้แสดงวิธีใช้เฟรมกล้องที่ฉายจากแว่นตาไปยังโทรศัพท์ให้คุณดูแล้ว
เธอแสดงให้คุณเห็นวิธีใช้ ML Kit, การจดจำคำพูด, การอ่านออกเสียงข้อความ และการอ่านออกเสียงข้อความของ ML Kit เพื่อเปลี่ยนข้อความกลับเป็นเสียงพูด
สิ่งที่เรายังขาดอยู่คือสมองที่อยู่ตรงกลาง ซึ่งสามารถดูเฟรมกล้องและคำถามร่วมกัน และหาคำตอบว่าผู้ใช้กำลังถามถึงอะไรกันแน่
สมองนั้นคือ Gemini Nano 4 ซึ่งสร้างขึ้นบน Gemma 4 ซึ่งเป็นโมเดลโอเพนซอร์สของ Google ที่ทำงานในโทรศัพท์โดยตรง
ดังนั้นในอุปกรณ์ Android รุ่นเรือธงที่รองรับ คุณจะได้รับประสิทธิภาพที่เพิ่มขึ้นโดยใช้ ML Kit API โดยตรง
ระบบ Android จะดูแลการนำส่งโมเดลให้คุณ คุณจึงไม่ต้องรวมโมเดลไว้ในแอป
สรุปก็คือ ไมโครโฟนจะรับเสียงเพื่อนำไปใช้ในการจดจำคำพูด จากนั้นเฟรมกล้องและข้อความถอดเสียงจะส่งไปยัง Gemini Nano 4 และ Gemini Nano 4 จะเขียนข้อมูลลงในการสังเคราะห์เสียงพูด แล้วส่งเสียงออกมาจากลำโพง
โมเดล 3 โมเดลที่เชื่อมโยงกันและทำงานในโทรศัพท์ของคุณทั้งหมด
และนี่คือเหตุผลบางประการที่ทำให้ Gemini Nano 4 เหมาะกับปัญหานี้เป็นพิเศษ
ประการแรกคือการมองเห็นและอ่านได้ในขั้นตอนเดียว
Gemini Nano 4 จะจัดการข้อความและรูปภาพในตัวผ่าน ML Kit คุณจึงไม่ต้องใช้เครื่องมือตรวจจับ เครื่องมือแยกประเภท และเครื่องมือแปลภาษา
ซึ่งเป็นโมเดลเดียวที่ดูเฟรมกล้องและคำถามไปพร้อมกัน
และอย่างที่ 2 คือการรองรับภาษาที่ครอบคลุม
Gemini Nano 4 ได้รับการฝึกด้วยภาษามากกว่า 140 ภาษา
นอกจากนี้ สำหรับติวเตอร์สอนภาษา การแปลเพียงอย่างเดียวยังไม่เพียงพอ
คุณต้องการสำนวน คุณต้องการการลงทะเบียนแบบเป็นทางการและแบบไม่เป็นทางการ และคุณยังต้องการประโยคตัวอย่างที่บุคคลจริงจะพูดด้วย
และสุดท้ายคือสามารถควบคุมได้มากพอที่จะมีบุคลิกเฉพาะตัว
การฝึกออกเสียงดังๆ อาจเป็นเรื่องที่น่ากลัว
และโมเดลนี้ทำตามพรอมต์ของระบบได้ดี ผู้ใช้จึงเลือกตัวตนของครูเองได้
และบางทีการมีผู้ช่วยที่อดทนและคอยให้กำลังใจอาจทำให้ผู้คนเต็มใจที่จะลองมากขึ้น
และยังมีอีกสิ่งหนึ่งที่ผมอยากไฮไลต์เกี่ยวกับการออกแบบ
ขณะที่การสนทนาดำเนินไปในโหมดแว่นตา แอปจะเขียนการ์ดคำศัพท์อย่างเงียบๆ ในเบื้องหลัง
คำ การแปล ประโยคตัวอย่าง และรูปภาพจากช่วงเวลาที่คุณถามคำถาม
ผู้ใช้ไม่จำเป็นต้องแตะอะไรด้วยตนเอง
พวกเขาแค่เดินและพูดคุยกัน
และหลังจากนั้นในวันเดียวกัน ทุกอย่างก็พร้อมให้ใช้งานในโทรศัพท์ ซึ่งเป็นสิ่งที่ฉันต้องการมากตอนที่อยู่ในโตเกียว
ดังนั้น การเน้นเสียงเป็นหลักจึงไม่ได้หมายความว่าต้องมีเฉพาะเสียงเท่านั้น
แว่นตาเป็นแพลตฟอร์มที่เหมาะสำหรับความอยากรู้อยากเห็น และโทรศัพท์เป็นแพลตฟอร์มที่เหมาะสำหรับการไตร่ตรองในภายหลัง
ปล่อยให้ช่วงเวลาแฮนด์ฟรีทิ้งบางสิ่งไว้เบื้องหลัง
ตอนนี้เรามาดูเคล็ดลับที่นำไปใช้ได้จริงในการสร้างประสบการณ์แบบเรียลไทม์ด้วย Gemini Nano 4 และ ML Kit ที่เราได้เรียนรู้จากการสร้างเดโมนี้กัน
เคล็ดลับที่ 1 ให้ความสำคัญกับเวลาในการตอบสนองแบบเรียลไทม์ด้วยการออกแบบการประมวลผล 2 รอบ
เมื่อคุณถามว่า "นั่นคืออะไร" บนแว่นตาที่มีระบบเสียง
ทุกช่วงเวลาที่เงียบจะสังเกตเห็นได้
ในตัวอย่างของครูสอนภาษา XR เราต้องการ 2 สิ่งจากการโต้ตอบของผู้ใช้แต่ละครั้งจากโมเดล ได้แก่ คำตอบที่พูดทันที และบัตรคำศัพท์ที่มีโครงสร้างเพื่อบันทึกลงในแอป
หากเราขอให้โมเดลทำ 2 อย่างพร้อมกันในการส่งผ่านข้อมูลครั้งเดียว โทเค็นทุกรายการที่ใช้ในการสร้างข้อมูลเบื้องหลังนั้นจะทำให้ประโยคแรกที่พูดล่าช้าออกไป
ดังนั้น เราจึงแยกสิ่งที่ผู้ใช้ต้องการในขณะนั้นออกจากสิ่งที่แอปต้องการในอีกไม่กี่วินาทีต่อมา
เราจึงแบ่งแต่ละรอบออกเป็น 2 ช่วง
ในรอบที่ 1 เราจะสตรีมเฟรมกล้องและคำถามสำหรับการตอบกลับด้วยเสียงพูดเพียงอย่างเดียว โดยจะทริกเกอร์การอ่านออกเสียงข้อความทันทีที่ประโยคแรกเสร็จสมบูรณ์
และเนื่องจาก TPU หรือ NPU นั้นสร้างคำตอบสั้นๆ เสร็จก่อนที่การอ่านออกเสียงข้อความจะพูดจบเป็นเวลานาน เราจึงมีช่วงเวลาหลายวินาทีที่ผู้ใช้ยังคงฟังอยู่ และชิปก็ไม่ได้ทำงานแล้ว
และเนื่องจาก Pass 1 ได้ระบุวัตถุในการตอบกลับแบบข้อความแล้ว Pass 2 จึงไม่จำเป็นต้องใช้อินพุตรูปภาพด้วยซ้ำ
เราเพียงส่งคำตอบของ Pass 1 กลับไปยังชิปที่ไม่มีการใช้งานนั้นเพื่อแยกและบันทึกการ์ดที่มีโครงสร้างก่อนที่ครูจะพูดจบด้วยซ้ำ
ตอนนี้เรามาซูมดู Pass 1 กัน ซึ่งจะนำเราไปสู่เคล็ดลับที่ 2
ใช้แท็ก XML ที่ใช้ทรัพยากรน้อยเมื่อสตรีม
เนื่องจาก Pass 1 สตรีมไปยังการอ่านออกเสียงข้อความโดยตรง คุณจึงไม่ต้องการให้ออบเจ็กต์ JSON ขนาดใหญ่ทำให้คำพูดแรกของคุณล่าช้า
แต่คุณต้องการข้อความธรรมดาที่สามารถพูดได้ทันทีด้วยแท็กในบรรทัดที่มีน้ำหนักเบาและเปิดอยู่เมื่อใดก็ตามที่แอปจำเป็นต้องดำเนินการระหว่างสตรีม
ฉันขอแสดงปัญหาที่ทำให้เราต้องมาถึงจุดนี้
จำของเล่นไม้ถ้วยและลูกบอลบนแผงขายของในตลาดที่ฉันพูดถึงได้ไหม
ในภาษาญี่ปุ่นเรียกว่า "เคนดามะ"
ในช่วงแรก โมเดลจดจำวัตถุได้จากเฟรมกล้องและให้คำตอบที่ถูกต้องว่า "นั่นคือเคนดามะ ของเล่นไม้"
เราจึงส่งประโยคที่สมบูรณ์นั้นไปยังเซสชันการอ่านออกเสียงข้อความภาษาอังกฤษ และนี่คือผลลัพธ์ที่ได้
นั่นคือเคนมิ ของเล่นไม้
ดูเหมือนว่าข้อมูลจะไม่ถูกต้อง
แต่ประโยคนั้นควรจะฟังดูเหมือน…
นั่นคือเคนดามะ ของเล่นไม้
แล้วทำไมถึงเป็นเช่นนั้น
เคนดามะเขียนด้วยอักษรฮิรางานะ "เคน" และอักษรคันจิ "ดามะ"
และเครื่องมืออ่านออกเสียงข้อความภาษาอังกฤษก็จัดการกับฮิรางานะที่ออกเสียงเหมือน "ken" ได้ดี แต่กลับไม่สามารถอ่านคันจิได้เลย และเปลี่ยน dama ให้เป็นคำที่ฟังดูเหมือน me
และปัญหานี้เกิดขึ้นในหลายภาษาในรูปแบบต่างๆ หากคุณไม่เปลี่ยนภาษา
ตัวอย่างเช่น ในภาษาสเปน คุณอาจได้ยินว่า…
ซู-โบ-ลู…
เมื่อคุณคาดหวังว่า…
Cebolla
และในทำนองเดียวกัน ในภาษาฝรั่งเศส คุณอาจได้ยินว่า…
PAIN oh chock-uh-lat…
เมื่อคุณคาดหวังว่า…
Pain au chocolat
ใช่ โดยปกติแล้วเซสชัน TTS จะออกแบบมาสำหรับภาษาเดียวในแต่ละครั้ง แต่ครูสอนภาษาจำเป็นต้องเปลี่ยนรหัสในทุกประโยค
ดังนั้นในรอบที่ 1 เราจะขอให้โมเดลรวมคำหรือวลีภาษาเป้าหมายทุกคำไว้ในแท็ก XML เป้าหมายขณะที่สตรีม
และเมื่อข้อมูลมาถึง ไคลเอ็นต์ของเราจะแยกข้อมูลตามเป้าหมาย กำหนดเส้นทางแต่ละช่วงไปยังภาษาที่ตรงกันสำหรับการอ่านออกเสียงข้อความ ซึ่งอาจเป็นภาษาญี่ปุ่น สเปน ฝรั่งเศส หรือภาษาใดก็ตามที่คุณกำลังเรียนรู้ จากนั้นจึงเล่นข้อมูลเหล่านั้นอย่างราบรื่น โดยเปลี่ยนสำเนียงกลางประโยค เพื่อให้คำอย่าง "เคนดามะ" ออกเสียงได้อย่างถูกต้อง
ดังนั้น สิ่งที่ได้จากตรงนี้คือโมเดลรู้แล้วว่าคำศัพท์ใดเป็นของภาษาใด คำใดเป็นของภาษาใด และการให้โมเดลติดแท็กช่วงเป้าหมายในบรรทัดขณะสตรีมนั้นสะอาดกว่าการประมวลผลภายหลังทั้งหมดนี้ด้วยตัวคุณเองในภายหลังมาก
และนี่คือโค้ดของรอบที่ 1
// 1. Pass 1 System + Turn Prompt (XrLanguageTeacherPersona.kt)
// System: "Always start immediately with <reply>...</reply> (strictly 1 to 3 sentences total).
// Inside <reply>, wrap every Japanese word AND every complete Japanese example sentence
// inside <target>...</target> tags (never output <card>)."
val pass1TurnPrompt = """
The student is looking at the camera view and asked: "$question". Answer in English
teaching Japanese inside <reply>...</reply>: if they refer to 'this'/'that' or point to
an object, ground it to the focal item in the camera frame; if they ask for a phrase or
greeting, teach that phrase.
""".trimIndent()
// 2. Stream multimodal dialogue (ImagePart + TextPart) and split <target> tags to TTS
val generativeModel = Generation.getClient()
val pass1Request = generateContentRequest(ImagePart(frame), TextPart(pass1TurnPrompt)) {
temperature = 1.0f; topK = 1; maxOutputTokens = 1024
}
generativeModel.generateContentStream(pass1Request).collect { chunk ->
val delta = chunk.candidates.firstOrNull()?.text.orEmpty()
streamReplyAndSplitTargetTagsToTts(delta) // Speaks 1st complete sentence immediately
}
// Streams: "<reply>That's <target>けん玉</target> — a wooden toy. <target>けん玉をください。</target></reply>"ที่ด้านบน เราใช้พรอมต์เพื่อสั่งให้โมเดลตอบกลับเป็นประโยค 1-3 ประโยคภายในแท็ก reply แล้วรวมคำภาษาญี่ปุ่นหรือประโยคตัวอย่างทุกประโยคไว้ภายในแท็ก target
จากนั้นเราจะส่งกล้อง ImagePart และพรอมต์ TextPart ไปยัง generateContentStream ของ ML Kit โดยแยกช่วงเป้าหมายเป็นอ่านออกเสียงข้อความเมื่อแต่ละประโยคมาถึง
ซึ่งนำเราไปสู่ขั้นตอนที่ 2 และเคล็ดลับที่ 3
แม้ว่าการใช้แท็ก XML แบบ Lightweight จะเหมาะกับสตรีมเสียงสด แต่ให้ใช้เอาต์พุตที่มีโครงสร้างซึ่งสร้างขึ้นเพื่อแสดงผลออบเจ็กต์ที่พิมพ์อย่างชัดเจนเมื่อบันทึกข้อมูลลงในแอป
เนื่องจากครูสอนภาษา XR ของเราเป็นการสนทนา ผู้ใช้จึงสามารถถามคำถามต่อเนื่องได้โดยไม่ต้องสอนคำศัพท์ใหม่
และเมื่อสอนคำศัพท์ ฐานข้อมูลจะต้องมีช่องพจนานุกรมที่ชัดเจนเพื่อสร้างการ์ดคำศัพท์
ดังนั้น แทนที่จะเขียนนิพจน์ทั่วไปที่เปราะบางเพื่อแยกวิเคราะห์ข้อความถอดเสียงของรอบที่ 1 เราจึงให้รอบที่ 2 แยกออบเจ็กต์ที่ปลอดภัยในการกำหนดประเภทในเบื้องหลัง
และเพื่อบังคับใช้สคีมาในรอบที่ 2 เราใช้ API เอาต์พุตที่มีโครงสร้างของ ML Kit ซึ่งเบื้องหลังจะใช้การถอดรหัสแบบจำกัด
import com.google.mlkit.genai.prompt.Generation
import com.google.mlkit.genai.prompt.generateTypedContentRequest
import com.google.mlkit.genai.schema.annotations.Generable
import com.google.mlkit.genai.schema.annotations.Guide
// Pass 2 schema (XrLanguageTeacherGenerableSchemas.kt):
@Generable(description = "Vocabulary flashcard for the taught word or short phrase")
data class GenerableVocabCard(
@Guide(description = TeacherResponseSchema.WORD_FIELD_DESCRIPTION) val word: String,
@Guide(description = "Native-language meaning or translation") val meaning: String,
@Guide(description = "Target-language example sentence") val example: String,
@Guide(description = "Native-language translation of the example sentence") val example_translation: String,
)
@Generable(description = "Optional vocabulary card extracted from a teacher reply")
data class GenerableOptionalVocabCardWrapper(
@Guide(description = "Vocabulary flashcard if a vocabulary term was taught, or null otherwise")
val card: GenerableVocabCard?
)คุณเพียงใส่คำอธิบายประกอบคลาสข้อมูลด้วย @Generable และแต่ละฟิลด์ด้วย @Guide เช่นเดียวกับตัวอย่างคลาสการ์ดคำศัพท์ที่นี่
และด้วยการรวมช่องการ์ดที่อนุญาตให้เป็นค่า Null ไว้ การส่งผ่านครั้งที่ 2 จะแสดงผลการ์ดที่มีข้อมูลเมื่อมีการสอนคำใหม่ หรือแสดงผลเป็น Null ในเทิร์นการสนทนาล้วนๆ
และนี่คือโค้ดของรอบที่ 2
// 1. Pass 1 Streamed Output -> Pass 2 Extraction Prompt (no image needed!)
val replyWithTargetTags =
"<reply>That's <target>けん玉</target> — a wooden toy. <target>けん玉をください。</target></reply>"
val pass2Prompt = """
If the teacher reply below teaches or introduces a specific Japanese vocabulary item, extract
only its core focal dictionary term into "card" with keys "word", "meaning", "example", and
"example_translation". If purely conversational with no focal term taught, set "card" to null:
$replyWithTargetTags
""".trimIndent()
// 2. Extract @Generable card on idle NPU while TTS audio plays
val pass2Request = generateContentRequest(TextPart(pass2Prompt)) {
temperature = 1.0f; topK = 1; maxOutputTokens = 256
}
val typedRequest =
generateTypedContentRequest(pass2Request, GenerableOptionalVocabCardWrapper::class)
val card: GenerableVocabCard? =
generativeModel.generateContent(typedRequest).candidates.firstOrNull()?.response?.card
// Extracts: {"word": "けん玉", "meaning": "a wooden toy", "example": "けん玉をください。", ...}ที่ด้านบน เราจะนำข้อความตอบกลับที่สตรีมของรอบที่ 1 มาใช้โดยไม่ต้องใช้รูปภาพ และสร้างพรอมต์การดึงข้อมูล
จากนั้น ขณะที่ผู้ใช้ยังคงฟังการอ่านออกเสียงข้อความอยู่ เราจะส่งพรอมต์และคลาส Wrapper ไปยัง generateTypedContentRequest และดึงข้อมูลบัตรคำศัพท์ที่สะอาดและไม่มีแท็ก หรือค่า Null หากไม่มีการสอนคำศัพท์
โอเค
เราได้พูดถึงเรื่องต่างๆ มากมาย
คุณอาจคิดว่า "ดีเลย มีอีกอย่างที่ดูดีบนเวทีแต่ฉันคงไม่มีวันได้แตะ" ซึ่งก็เป็นเรื่องที่เข้าใจได้
ไม่ใช่กิจวัตรนี้
XR Language Teacher จะมาพร้อมกับแอป Google AI Edge Gallery ควบคู่ไปกับประสบการณ์การใช้งาน AI ในอุปกรณ์อื่นๆ
คุณสามารถดาวน์โหลดแอปนี้ได้ตั้งแต่วันนี้ ติดตั้งลงในโทรศัพท์ของคุณเอง แล้วเล็งไปที่สิ่งใดก็ได้ที่คุณต้องการเรียนรู้วิธีพูดเป็นภาษาอื่นได้ตั้งแต่วันนี้
และหากต้องการทราบวิธีสร้าง การติดตั้งใช้งานทั้งหมดนี้เป็นโอเพนซอร์สใน GitHub
และไม่ต้องกังวลหากผู้ใช้บางรายยังไม่มีแว่นตาที่มีจอแสดงผลหรือแว่นตาที่มีระบบเสียง หรืออุปกรณ์ Android รุ่นเรือธงที่รองรับ
ฐานของโค้ดของเรายังแสดงตัวอย่างวิธีปรับเปลี่ยนประสบการณ์เดียวกันนี้ให้เหมาะกับประสบการณ์การใช้งานบนอุปกรณ์เคลื่อนที่เท่านั้นด้วย
และหากต้องการเข้าถึงแพลตฟอร์มอื่นๆ ด้วย โค้ดแอป Google AI Edge Gallery ยังแสดงวิธีสร้างฟีเจอร์นี้โดยใช้รันไทม์ Google LiteRT และ MediaPipe Tasks API ด้วย
หรือหากต้องการนำโมเดลเสียงหรือโมเดลภาษาในอุปกรณ์แบบโอเพนซอร์สอื่นๆ มาใช้ คุณก็ดูวิธีดำเนินการได้จากโค้ดเช่นกัน
เราจึงเปิดตัว XR Language Teacher เป็นโอเพนซอร์สเพื่อให้คุณสร้างต้นแบบได้ง่ายขึ้น
ชี้ Agent ที่คุณเลือกไปที่ที่เก็บ GitHub ของเราเพื่อเปลี่ยนประสบการณ์การใช้งานแบบเรียลไทม์ในอุปกรณ์ที่ทำงานด้วยระบบ AI นี้ให้เป็นอย่างอื่น
อาจเป็นไกด์นำเที่ยวพิพิธภัณฑ์ ผู้ช่วยทำอาหารที่ดูเคาน์เตอร์ของคุณ คำแนะนำการซ่อม ผู้บรรยายการช่วยเหลือพิเศษ หรือเครื่องมือตรวจสอบภาคสนาม
โมเดล, รันไทม์, การติดตั้งใช้งานโอเพนซอร์ส และเครื่องมือทั้งหมดพร้อมให้ใช้งานแล้ว เพื่อให้คุณเตรียมพร้อมสำหรับการเปิดตัวแว่นตาอัจฉริยะที่กำลังจะมาถึงในฤดูใบไม้ร่วงนี้
เราอยากเห็นว่าคุณจะสร้างอะไรจากฟีเจอร์นี้
สรุปแล้ว วันนี้เราได้แสดงตัวอย่างที่มีประสิทธิภาพของสิ่งที่เป็นไปได้ด้วยโมเดล AI ที่ทำงานบนอุปกรณ์ได้อย่างราบรื่น
มาดูวิธีที่จะช่วยคุณเริ่มต้นใช้งานกันดีกว่า
ก่อนอื่น ให้ดูแหล่งข้อมูลบนหน้าจอ
ดาวน์โหลดแอป AI Edge Gallery เพื่อลองใช้เดโมและดูโค้ดอ้างอิงใน GitHub
และสแกนคิวอาร์โค้ดเพื่อเข้าถึง Android XR SDK, ML Kit API และข้อเสนอ Google AI Edge
2 เริ่มทดลองใช้บนโทรศัพท์ได้เลยวันนี้
เราได้พูดถึงหลายๆ สิ่งที่สามารถสร้างขึ้นด้วยประสบการณ์การใช้งานอุปกรณ์เคลื่อนที่เป็นอันดับแรกไปแล้ว
และข้อที่ 3 ให้ดูแอปบนอุปกรณ์เคลื่อนที่ที่คุณเผยแพร่อยู่แล้ว และถามตัวเองว่าฟีเจอร์หลักใดบ้างที่จะทำงานได้ดีขึ้นแบบแฮนด์ฟรี
เพียงเพิ่มกิจกรรมที่ฉายลงใน APK ที่มีอยู่ คุณก็สามารถนำประสบการณ์การใช้งานนั้นมาใช้กับแว่นตาได้ด้วย
ขอขอบคุณที่มาร่วมเซสชันของเราในวันนี้
เราอยากเห็นแล้วว่าคุณจะสร้างประสบการณ์การใช้งานที่เน้นเสียงเป็นหลักอันน่าทึ่งอะไรบ้างบน Android และสำหรับแว่นตาอัจฉริยะ
ขอขอบคุณ
dependencies {
implementation("androidx.xr.projected:projected:1.0.0-alpha13")
}class ProjectedMainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// Glasses content
}
}
}<activity
android:name=".ProjectedMainActivity"
android:exported="false"
android:requiredDisplayCategory="android.hardware.display.category.XR_PROJECTED"
android:label="Example activity for audio glasses">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.XR_PROJECTED_LAUNCHER"/>
</intent-filter>
</activity>// Continuously monitor whether the glasses are connected
val isGlassesConnected by ProjectedContext.isProjectedDeviceConnected(
context,
scope.coroutineContext
).collectAsStateWithLifecycle(initialValue = false)
// Launch the activity
val options = ProjectedContext.createProjectedActivityOptions(context)
val intent = Intent(context, ProjectedMainActivity::class.java)
context.startActivity(intent, options.toBundle())
// Check if the device is audio only or has a display
val projectedDeviceController = ProjectedDeviceController.create(this@ProjectedMainActivity)
isVisualUiSupported = projectedDeviceController.capabilities.contains(CAPABILITY_VISUAL_UI)// In a projected activity
private fun requestProjectedPermissions() {
lifecycleScope.launch {
try {
ProjectedActivityCompat.requestPermissions(
this@PermissionsFromProjectedActivity,
arrayOf(Manifest.permission.CAMERA),
// CAMERA_PERMISSION_REQUEST_CODE is a developer defined constant
requestCode = CAMERA_PERMISSION_REQUEST_CODE,
)
} catch (e: IllegalStateException) {
// Thrown when the projected system service can't be bound.
Log.e(TAG, "Request failed: projected service unavailable.", e)
}
}
}android skills add projected-hardware
android skills add --alldependencies {
// ML Kit GenAI TTS SDK
implementation("com.google.mlkit:genai-text-to-speech:1.0.0-alpha1")
}// 1. Initializing the SpeechSynthesizer client.
val options = SpeechSynthesizerOptions.Builder(Locale.US).build()
val client = SpeechSynthesis.getClient(options)
// 2. Check and download the voice model.
val voiceInfo = client.getSupportedVoices().firstOrNull()
if (voiceInfo != null && client.checkStatus(voiceInfo) == FeatureStatus.DOWNLOADABLE) {
client.download(voiceInfo).collect { status -> println("Download status: $status") }
} else if (voiceInfo == null) {
// Tip: Fallback to system TTS if ML Kit voice is unavailable
}// 3. Construct the SpeechSynthesizerRequest
val request = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Text("Hello world", voiceInfo),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build()
)
// 4. Speak and collect status
client.synthesize(request).collect { response ->
when (response) {
is SpeechSynthesizerResponse.StatusUpdate -> println("Playback status: ${response.status}")
}
}// 4. Speak and collect status
client.synthesize(request).collect { response ->
when (response) {
is SpeechSynthesizerResponse.StatusUpdate -> println("Playback status: ${response.status}")
}
}
// 5. Release resources when finished (e.g., on Activity destroy)
override fun onDestroy() {
super.onDestroy()
client.close()
}// 1. Register Earcon resource with Builder
SpeechSynthesizerOptions.Builder(Locale.US)
.addEarcon(
name = "shutter_sound",
Uri = "path/to/earcon.file".toUri()
)
.build()
// 2. Request Earcon playback
val request = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Earcon(
name = "shutter_sound"
),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes
.USAGE_ASSISTANCE_SONIFICATION)
.setContentType(AudioAttributes
.CONTENT_TYPE_SONIFICATION)
.build()
)dependencies {
// ML Kit Speech Recognition
implementation("com.google.mlkit:genai-speech-recognition:1.0.0-alpha1")
}suspend fun startMyRecognition() {
// choose locale and mode
val options: SpeechRecognizerOptions =
speechRecognizerOptions {
locale = Locale.US
preferredMode = SpeechRecognizerOptions.Mode.MODE_ADVANCED
}
val speechRecognizer: SpeechRecognizer = SpeechRecognition.getClient(options)
val request: SpeechRecognizerRequest =
speechRecognizerRequest { audioSource = AudioSource.fromMic() }
...speechRecognizer.startRecognition(request).collect { response ->
when (response) {
is SpeechRecognizerResponse.PartialTextResponse -> {
// Real-time: Act immediately if trigger phrase is heard
handleResponse(response.text, speechRecognizer)
}
is SpeechRecognizerResponse.FinalTextResponse -> {
// Final result and recognition
handleResponse(response.text, speechRecognizer)
}
is SpeechRecognizerResponse.CompletedResponse -> {
resetListeningUi()
}
is SpeechRecognizerResponse.ErrorResponse -> {
Log.e("SpeechRec", "Error: ${response.e.errorCode}", response.e)
resetListeningUi()
}
...// Helper function to check for multiple phrases and trigger the right action
private suspend fun handleResponse(text: String, recognizer: SpeechRecognizer) {
val savePhrases = listOf("save", "bookmark", "remember")
if (savePhrases.any { text.contains(it) }) {
// 1. Update app state immediately
saveItemForLater()
// 2. Stop the recognizer to enforce the action and prevent TTS echo
recognizer.stopRecognition()
// 3. Play the mute earcon for immediate, non-verbal confirmation
playEarcon()
// 4. Provide voice feedback
playAudioFeedback("Saved")
}
}// 1. Pass 1 System + Turn Prompt (XrLanguageTeacherPersona.kt)
// System: "Always start immediately with <reply>...</reply> (strictly 1 to 3 sentences total).
// Inside <reply>, wrap every Japanese word AND every complete Japanese example sentence
// inside <target>...</target> tags (never output <card>)."
val pass1TurnPrompt = """
The student is looking at the camera view and asked: "$question". Answer in English
teaching Japanese inside <reply>...</reply>: if they refer to 'this'/'that' or point to
an object, ground it to the focal item in the camera frame; if they ask for a phrase or
greeting, teach that phrase.
""".trimIndent()
// 2. Stream multimodal dialogue (ImagePart + TextPart) and split <target> tags to TTS
val generativeModel = Generation.getClient()
val pass1Request = generateContentRequest(ImagePart(frame), TextPart(pass1TurnPrompt)) {
temperature = 1.0f; topK = 1; maxOutputTokens = 1024
}
generativeModel.generateContentStream(pass1Request).collect { chunk ->
val delta = chunk.candidates.firstOrNull()?.text.orEmpty()
streamReplyAndSplitTargetTagsToTts(delta) // Speaks 1st complete sentence immediately
}
// Streams: "<reply>That's <target>けん玉</target> — a wooden toy. <target>けん玉をください。</target></reply>"import com.google.mlkit.genai.prompt.Generation
import com.google.mlkit.genai.prompt.generateTypedContentRequest
import com.google.mlkit.genai.schema.annotations.Generable
import com.google.mlkit.genai.schema.annotations.Guide
// Pass 2 schema (XrLanguageTeacherGenerableSchemas.kt):
@Generable(description = "Vocabulary flashcard for the taught word or short phrase")
data class GenerableVocabCard(
@Guide(description = TeacherResponseSchema.WORD_FIELD_DESCRIPTION) val word: String,
@Guide(description = "Native-language meaning or translation") val meaning: String,
@Guide(description = "Target-language example sentence") val example: String,
@Guide(description = "Native-language translation of the example sentence") val example_translation: String,
)
@Generable(description = "Optional vocabulary card extracted from a teacher reply")
data class GenerableOptionalVocabCardWrapper(
@Guide(description = "Vocabulary flashcard if a vocabulary term was taught, or null otherwise")
val card: GenerableVocabCard?
)// 1. Pass 1 Streamed Output -> Pass 2 Extraction Prompt (no image needed!)
val replyWithTargetTags =
"<reply>That's <target>けん玉</target> — a wooden toy. <target>けん玉をください。</target></reply>"
val pass2Prompt = """
If the teacher reply below teaches or introduces a specific Japanese vocabulary item, extract
only its core focal dictionary term into "card" with keys "word", "meaning", "example", and
"example_translation". If purely conversational with no focal term taught, set "card" to null:
$replyWithTargetTags
""".trimIndent()
// 2. Extract @Generable card on idle NPU while TTS audio plays
val pass2Request = generateContentRequest(TextPart(pass2Prompt)) {
temperature = 1.0f; topK = 1; maxOutputTokens = 256
}
val typedRequest =
generateTypedContentRequest(pass2Request, GenerableOptionalVocabCardWrapper::class)
val card: GenerableVocabCard? =
generativeModel.generateContent(typedRequest).candidates.firstOrNull()?.response?.card
// Extracts: {"word": "けん玉", "meaning": "a wooden toy", "example": "けん玉をください。", ...}