با نحوه ساختن تجربههای صوتی دستآزاد و قدرتمند بااستفاده از هوش مصنوعی دروندستگاهی آشنا شوید. همه چیزهایی را که نیاز دارید پوشش خواهیم داد: راهاندازی فعالیت عینک، ضبط فیدهای میکروفون و دوربین، و استفاده از صدا همراه با سایر نشانههای صوتی بهعنوان واسط کاربری نهایی شما. ازطریق نمایش هیجانانگیز و واقعی «معلم زبان XR» متنباز که در برنامه Google AI Edge Gallery ساخته شده است، یاد بگیرید که Gemini Nano 4 (ساختهشده بر Gemma 4 و بهینهشده برای دستگاههای Android) و ML Kit را هماهنگ کنید و از زمان اجرای LiteRT برای ساختن تجربههای سریع XR که بهطور طبیعی میشنوند، میبینند، و صحبت میکنند بهره ببرید.
آوانویسی
سلام، خوش آمدید، همگی.
دیدن همه شما امروز عالی است.
از اینکه آمدید سپاسگزاریم.
من ایمی هستم.
امروز همکارم، آلیس، به من ملحق میشود و قرار است درباره اینکه چگونه میتوانید بااستفاده از Gemini Nano 4 تجربههای فوقالعادهای با اولویت صدا برای افزاردید هوشمند بسازید صحبت کنیم.
پس بیایید شروع کنیم.
بنابراین وقتی درباره افزاردید هوشمند صحبت میکنیم، منظورمان عینکهای سبک و قابلحملی است که برای استفاده در تمام طول روز طراحی شدهاند.
این دسته شامل عینکهای دارای نمایشگر و عینکهای فقط صوتی میشود و با این دستگاهها، سختافزارهای متنوعی را در اینجا خواهید دید.
بنابراین اولین چیزی که متوجه خواهید شد این است که آنها یک صفحه لمسی خواهند داشت که میتواند برای ورودی کاربر، چیزهایی مانند ضربه زدن و تند کشیدن استفاده شود.
همچنین بلندگو، دوربین، و میکروفون دارند.
و سپس آنها همچنین میتوانند نمایشگر داشته باشند یا نداشته باشند، یا احتمالاً ترکیبی از اینها.
امروز، ما درباره اولویت صوتی و نحوه ساختن تجربههای جذاب، هوشمند، و درحال حرکت صحبت میکنیم.
و یکی از چیزهایی که در حالت فقط صوتی جالب است این است که صدا به رابط کاربری شما تبدیل میشود.
و هوش مصنوعی دروندستگاهی برای توسعهدهندگانی که بهدنبال تجربههای آفلاین و درحال حرکت هستند عالی است.
و خبرهای خوبی برایتان داریم.
بنابراین اگر درحالحاضر برای Android میسازید، درحالحاضر برای Android XR هم میسازید.
برنامههای Android موجود بهگونهای طراحی شدهاند که بهخوبی با عینکهای صوتی سازگار شوند و اعلانهای گفتاری و سایر صداها را ازطریق بلوتوث ارائه دهند.
و اگر میخواهید فراتر بروید و برنامهتان را برای واقعیت گسترشیافته متمایز کنید، راههای زیادی برای انجام این کار وجود دارد.
بنابراین در این گفتگو، ما درباره مدلهای دروندستگاهی صحبت میکنیم، اما این بههیچوجه فهرست کاملی نیست.
ما-- مدلهای ابری هم داریم.
چیزهایی مثل Gemini Live API وجود دارد.
عوامل سیستمی وجود دارد که میتوانید با آنها ادغام کنید، مانند Gemini، و همه اینها میتوانند به شما کمک کنند تا تجربهای را بسازید که برای شما بهترین است.
پس چگونه این تجربه را که بهطور طبیعی میشنود، میبیند، و صحبت میکند میسازید؟
امروز میخواهیم شما را در ساختن این تجربههای دستآزاد و قدرتمند که با هوش مصنوعی دروندستگاهی کار میکنند راهنمایی کنیم.
میخواهم شما را با اصول اولیه آشنا کنم: نحوه ساختن برای عینک، نحوه راهاندازی فعالیت، دریافت اجازههای موردنیاز، و سپس نحوه استفاده از ML Kit برای واسط کاربری مکالمهای.
And then I'll hand it over to Alice, and she's going to talk you through some real examples that use Gemini Nano 4.
پس بیایید ادامه دهیم و به کد بپردازیم.
بنابراین با عینک، تلفن است که کار سنگین را انجام میدهد و محتوای عینک از تلفن به عینک پخش میشود.
بنابراین دو چیز وجود دارد که برای پل زدن این دستگاهها هنگام راهاندازی اولیه نیاز دارید.
آنچه در ابتدا نیاز دارید کتابخانه Jetpack Projected است.
باید این وابستگی را اضافه کنید و این همان چیزی است که حاوی میاناهای برنامهسازی کاربردی (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 هم در برنامه موجود و هم در این فعالیت جدید استفاده میکنید تا دو دستگاه بتوانند درصورت نیاز بهراحتی با یکدیگر ارتباط برقرار کنند.
بنابراین، درست مانند هر فعالیت عادی که دیدهاید، این را در مانیفست خود اعلام خواهید کرد.
<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 اجازه میدهد برنامه شما را کشف کند تا کاربران بتوانند آن را بهصورت دستآزاد درحال حرکت راهاندازی کنند.
بسیار خوب.
بنابراین، همانطور که گفتم، وقتی آن فعالیت را راهاندازی کردید، از آن کتابخانه Projected برای تقویت تجربه برنامه استفاده خواهید کرد.
این کتابخانه حاوی همه ابزارهایی است که برای ساختن تجربه کامل عینک نیاز دارید.
بنابراین بیایید چند 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 را بهطور کلی دنبال کنید.
اجازههای لازم را در مانیفست اعلام کنید، اجازهها را در بافتی که به آنها نیاز است درخواست کنید، و فقط اجازههایی را درخواست کنید که واقعاً برای عملکرد برنامهتان به آنها نیاز دارید.
بنابراین، البته، ما این را در سایت خود در developer.android.com مستند کردهایم.
اما مهارت کاملاً جدیدی هم داریم، مهارت سختافزار پیشبینیشده.
میتوانید از این مهارت با Android CLI در Android Studio برای راهاندازی گردش کار اجازهها استفاده کنید و میتوانید به این مهارت و سپس به همه مهارتهای دیگر Android نیز دسترسی پیدا کنید.
android skills add projected-hardware
android skills add --allبسیار خوب، اکنون که متصل شدهایم، از قابلیتهای سختافزاری خود آگاه هستیم، و اجازههای لازم را داریم، بیایید درباره ابزارهایی صحبت کنیم که برای ساختن آن تجربه صوتی دراختیار دارید.
بنابراین هنگام طراحی تجربه اولویت با صدا، میخواهیم مطمئن شویم که تجربهای با کیفیت بالا برای کاربران ایجاد میکنیم.
و این میتواند شامل چند تعامل کلیدی باشد.
بنابراین، اولین مورد استفاده از صدا بهعنوان خروجی است، یعنی به صدای شما اجازه میدهد بهطور طبیعی با کاربرتان صحبت کند.
مورد بعدی استفاده از نمادهای شنیداری برای ارسال سیگنالهای صوتی خاص به کاربر است.
و سپس ورودی کاربر را نیز مدیریت میکند، چه ازطریق صفحه لمسی بااستفاده از تکضرب و تند کشیدن باشد، یا اگر بخواهید به کاربر اجازه دهید با برنامهتان صحبت کند.
بنابراین اولین چیزی که میخواهیم درباره آن صحبت کنیم، استفاده از صدا بهعنوان خروجی است.
بنابراین برای این کار، میخواهم کتابخانه جدید «نوشتار به گفتار» با «هوش مصنوعی زایا» در ML Kit را معرفی کنم.
این ویژگی اکنون در نسخه آلفا دردسترس است و در دستگاههای Pixel و Samsung منتخب ارائه شده است.
این چیزی است که ما بسیار درباره آن هیجانزده هستیم، بهویژه برای این تجربههای صوتیمحور.
اگر قبلاً با «نوشتار به گفتار» کار کرده باشید، احتمالاً میدانید که یکی از چالشهای عمده این است که «صدای سیستم» همیشه آنطور که میخواهید طبیعی بهنظر نمیرسد.
و اتصال به مدلهای ابری برای صداهای دیگر میتواند باعث تأخیر احتمالی یا هزینههای اضافی شود.
بنابراین ML Kit درمقایسه با TTS سیستم، گفتاری بسیار طبیعیتر به شما ارائه میدهد.
و ML Kit هشت صدای منحصربهفرد ارائه میدهد که میتوانید از بین آنها انتخاب کنید و هرکدام ویژگیهای متفاوتی دارند تا نیازهای برنامهتان را برآورده کنند.
و همچنین ML Kit با ۲۱ منطقه زبانی مختلف که میتوانید از بین آنها انتخاب کنید، پشتیبانی زبانی قدرتمندی ارائه میدهد.
و علاوهبر آن، یکی از مزایای بزرگ در اینجا این است که پساز آن بارگیری اولیه، این کاملاً روی دستگاه کار خواهد کرد.
بنابراین این مدل در سیستم بارگیری میشود و در برنامهها همرسانی میشود، بنابراین اندازه APK شما را افزایش نمیدهد.
و نه تنها بهصورت آفلاین دردسترس است، که بهویژه برای کاربران عینک که درحال حرکت هستند مهم است، ممکن است Wi-Fi نداشته باشند یا در مناطق با پوشش ضعیف باشند، بلکه احتمالاً دوباره عملکرد بهتری خواهید دید، زیرا مجبور نیستید آن تماس رفتوبرگشت را به سرور برقرار کنید.
بسیار خوب.
پس بیایید به جزئیات کتابخانه و نحوه ساختن با آن نگاهی بیندازیم.
بنابراین ابتدا، البته، فقط باید وابستگی را اضافه کنید.
dependencies {
// ML Kit GenAI TTS SDK
implementation("com.google.mlkit:genai-text-to-speech:1.0.0-alpha1")
}بنابراین در اینجا تنها چیزی که نیاز دارید کیت توسعه نرمافزار «نوشتار به گفتار» ML Kit است.
و سپس میتوانید نوشتن کدتان را شروع کنید.
بنابراین، کتابخانه GenAI ML Kit شامل میاناهای برنامهسازی کاربردی «ترکیبکننده گفتار» است.
// 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
}این کار روشی ساده برای پیادهسازی صداهای انتخابیتان ارائه میدهد.
و اولین کاری که در اینجا انجام میدهید این است که کلاینت خود را بااستفاده از همان ترکیبکننده گفتار مقداردهی اولیه کنید و منطقهای را که انتخاب میکنید تنظیم کنید.
سپس، وضعیت بارگیری را بررسی میکنید و صدا را انتخاب میکنید.
در این مثال، ما صدای اول را انتخاب میکنیم، اما میخواهم در اینجا به این نکته هم اشاره کنم که ما از firstOrNull استفاده میکنیم.
همانطور که گفتم، ML Kit به تلفن توانمند خاصی نیاز دارد، بنابراین یا دستگاههای Samsung یا Pixel.
بنابراین اگر مقدار تهی دریافت کنید، ممکن است به این معنی باشد که کاربر از تلفن سازگار استفاده نمیکند.
And then you just want to make sure that you have your fallback in place, whether that's going back to the System Text To Speech or like playing a recorded message or something like that.
بسیار خوب، و سپس از ترکیبکننده گفتار 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 استفاده کنید و چه خودتان آنها را ایجاد کنید.
بنابراین باید در نحوه استفاده از نمادهای شنیداری و چگونگی استفاده از آنها دقت کنید.
بنابراین، چند نکته را باید درنظر داشته باشید، مثلاً برای شروع، سعی کنید از نمادهای شنیداری بیشازحد استفاده نکنید.
صداهای یکسان را بارها و بارها بازپخش نکنید، این کار میتواند باعث خستگی گوش شود.
و بهخاطر داشته باشید که نمادهای شنیداری شما باید عالی و شهودی باشند، همچنین هنگام پیادهسازی آنها از زیروبم و ریتمهای آشنا استفاده کنید.
و سپس، سعی کنید از نمادهای شنیداری بیشازحد پیچیده که نیاز به توضیح دارند اجتناب کنید، اینها ممکن است بهعنوان اعلان صوتی مناسبتر باشند.
و همیشه بهخاطر داشته باشید که آنچه ممکن است در استودیو یا در سیستم شما، در بلندگوهای رایانهتان، ظریف بهنظر برسد، میتواند پساز بازپخش مکرر برای کاربران شما آزاردهنده شود.
بنابراین تمام تلاش خود را بکنید تا کاربران را با صداهایی که ممکن است آنها را گیج کند یا باعث شود هشدارهای مهم را نادیده بگیرند، تحت فشار قرار ندهید.
بسیار خوب.
بنابراین اکنون میتوانیم نشانههای صوتی و بازخورد ارائه دهیم، چه ازطریق گفتار طبیعی باشد یا بااستفاده از نمادهای شنیداری.
اما درباره ورودی چطور؟
بنابراین، البته یک صفحه لمسی وجود دارد که یا دکمههای اختصاصی نیز روی دستگاه وجود دارد که میتوانید از آنها برای ایجاد تعاملات ضربه زدن و تند کشیدن استفاده کنید.
اما ممکن است بخواهید این واسط کاربر مکالمهای را نیز مجاز کنید، برای این کار میتوانید از GenAI Speech Recognition API در ML Kit استفاده کنید.
درحالحاضر در نسخه آلفا و دردسترس در دستگاههای منتخب، اما این ویژگی پایه در دو حالت متمایز اجرا میشود.
بنابراین حالت پایه وجود دارد که از تشخیص گفتار دروندستگاهی سنتی استفاده میکند و در دستگاههای Android با سطح میانای برنامهسازی کاربردی ۳۱ و بالاتر دردسترس است.
و سپس حالت پیشرفته نیز وجود دارد که از Gemini Nano استفاده میکند، این حالت در دستگاههای Pixel 10 و بالاتر ارائه میشود و با این حالت کیفیت بهتر و پوشش زبانی بهتری نیز خواهید دید.
بنابراین SpeechRecognizer از الگوی کد بسیار مشابهی با SpeechSynthesizer پیروی میکند.
کارخواهتان را ایجاد میکنید، دردسترس بودن مدل را بررسی میکنید، درصورت نیاز بارگیری میکنید، درخواست را میسازید، و منابعتان را منتشر میکنید.
بنابراین بیایید فقط روی تفاوتهای اصلی تمرکز کنیم، جایی که درخواست خود را ایجاد میکنید، صدای میکروفون را ضبط میکنید، و سپس نتیجه را پردازش میکنید.
دوباره، باید وابستگیهای لازم را اضافه کنید.
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، چهار پاسخ بالقوه دارید: جزئی، نهایی، خطا، و کامل.
Partial is gonna be looking at the real-time stream, so that is used for think like hotwords or keywords.
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 تشخیص دهد کاربر از یکی از این عبارتها استفاده کرده است، تشخیص گفتار را متوقف میکند، محتوا را ذخیره میکند، نماد شنیداری سفارشی پخش میکند، و بااستفاده از «نوشتار به گفتار» هوش مصنوعی زایای ML Kit به کاربر میگوید که محتوا ذخیره شده است.
بسیارخوب.
بنابراین، ما چیزهای زیادی را پشت سر گذاشتیم.
ممکن است کمی انتزاعی بهنظر برسد.
اگر مثل من باشید، میخواهید چند مثال واقعی ببینید.
و من و آلیس میخواهیم دقیقاً همین را به شما نشان دهیم.
او شما را با «معلم زبان واقعیت گسترده» در برنامه «گالری Google AI Edge» آشنا خواهد کرد.
تیم آلیس آن را ساخته است و او دقیقاً به شما نشان خواهد داد که چگونه کار میکند.
نوبت شما است.
ممنون، سارا.
آره.
بنابراین، دوست دارم با چیزی شروع کنم که بسیاری از شما آن را تشخیص میدهید.
من درحال یادگیری زبان ژاپنی هستم و ساعتها وقتم را در برنامههای یادگیری زبان، فلشکارتها، زنجیرهها، و همه این چیزها گذراندهام.
و بعد، وقتی به توکیو میرسم، وارد یک بازار میشوم و متوجه میشوم که تقریباً هیچکدام از چیزهایی که میبینم با واژگانی که مطالعه کردهام مطابقت ندارد.
یک اسباببازی چوبی فنجان و توپ روی یک غرفه بازار وجود دارد که من آن را از روی ظاهر میشناسم، اما نمیدانم چگونه به زبان ژاپنی صحبت کنم.
در ویترین مغازهای زیورآلاتی هست که میخواهم بخرم اما نامش را نمیدانم.
این دقیقاً همان لحظهای است که بیشترین انگیزه را برای یادگیری کلمه دارم.
نه اینکه روی مبل لم بدهم و به برنامه زبان نگاه کنم.
در آن لحظه، من به چیزی نگاه میکنم که میخواهم آن را یاد بگیرم.
بنابراین، این چیزی است که میخواهم.
میخواهم تمام روز عینک هوشمندم را به چشم بزنم و فقط قدم بزنم.
و هروقت چیزی توجه من را جلب کرد، فقط با صدای بلند درباره آن بپرسید.
و بعد، شب، وقتی به هتلم برمیگردم، هرچیزی که دربارهاش پرسیده بودم مثل کارتهای واژگان در تلفنم منتظرم است.
و این چیزی است که ما ساختیم.
بیایید کلیپ کوتاهی تماشا کنیم.
بنابراین، ما به این «معلم زبان واقعیت گسترده» میگوییم.
سلام.
امروز میخواهید درباره چه چیزی یاد بگیرید؟
و میتوانید تجربه یادگیریتان را در تنظیمات معلم سفارشیسازی کنید.
برای مثال، زبانی را که میخواهید یاد بگیرید انتخاب کنید و شخصیت معلم خاصی را که برایتان مناسب است انتخاب کنید.
آه، بله.
و میتوانید بین حالت عینک و تلفن جابهجا شوید.
چطور میتوانم این انگورها را به اسپانیایی بگویم؟
با نگاه کردن به این، به آن انگورهای خوشمزه uvas میگویند.
به اسپانیایی، میتوانید بگویید «Estas uvas son deliciosas» تا آنها را توصیف کنید.
میخواهم بهطور خاص بگویم انگور سبز.
بگذارید نگاهی بیندازم.
برای مشخص کردن انگور سبز، میتوانید بگویید «Uvas verdes».
Me gustan las uvas verdes» روشی عالی برای گفتن این است که آنها را دوست دارید.
این را به فرانسوی چطور میگویند و چطور میتوانم سه تا از آن را درخواست کنم؟
بیایید این را بررسی کنیم.
به این شیرینیهای خوشمزه «مادلین» میگویند.
در زبان فرانسوی، برای درخواست سه عدد، میتوانید بگویید: «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، عملکرد بهینهشدهای دریافت کنید.
سیستم Android تحویل مدل را برای شما انجام میدهد، بنابراین نیازی نیست چیزی را در برنامهتان بستهبندی کنید.
بنابراین برای جمعبندی همه اینها، میکروفون وارد تشخیص گفتار میشود و قاب دوربین و ترانویسی با هم وارد Gemini Nano 4 میشوند و Gemini Nano 4 در ترکیب گفتار مینویسد و سپس از بلندگو خارج میشود.
سه مدل، زنجیرهای، همه بهصورت محلی در تلفن شما اجرا میشوند.
و در اینجا چند دلیل وجود دارد که چرا Gemini Nano 4 بهطور خاص برای این مشکل مناسب است.
اول، در یک مرحله میبیند و میخواند.
ازطریق ML Kit، Gemini Nano 4 نوشتار و تصاویر را بهصورت بومی مدیریت میکند، بنابراین از آشکارساز، سپس طبقهبندیکننده، و سپس مترجم استفاده نمیکنید.
این مدل بهطور همزمان به قاب دوربین و سؤال نگاه میکند.
و دومین مورد پشتیبانی گسترده آن از زبانهای مختلف است.
Gemini Nano 4 با بیشاز ۱۴۰ زبان آموزش دیده است.
و همچنین، برای مربی زبان، ترجمه بهتنهایی کافی نیست.
اصطلاحات، تفاوت بین زبان رسمی و غیررسمی، و همچنین جملات نمونهای که یک فرد واقعی ممکن است بگوید را میخواهید.
و درنهایت، آنقدر قابلهدایت است که میتواند شخصیت داشته باشد.
تمرین تلفظ با صدای بلند میتواند ترسناک باشد.
و این مدل بهخوبی از پیامواره سیستم پیروی میکند، بنابراین کاربر میتواند شخصیت معلم خود را انتخاب کند.
و شاید یک لحن صبور و مشوق بتواند افراد را به امتحان کردن ترغیب کند.
و این هم نکته دیگری که میخواهم درباره طراحی برجسته کنم.
درحالیکه مکالمه در حالت عینک انجام میشود، برنامه بیصدا درحال نوشتن کارتهای واژگان در پسزمینه است.
کلمه، ترجمه، جمله نمونه، و عکس مربوط به لحظهای که سؤال را میپرسید.
کاربر نیازی ندارد بهصورت دستی روی چیزی تکضرب بزند.
آنها فقط دارند راه میروند و صحبت میکنند.
و بعداً در همان روز، همه چیز در تلفنشان منتظر آنها است، که دقیقاً همان چیزی است که وقتی در توکیو بودم میخواستم.
بنابراین، اولویت دادن به صدا لزوماً بهمعنای فقط صدا نیست.
عینک سطح مناسبی برای کنجکاوی است و تلفن سطح مناسبی برای بازتاب پساز آن است.
بگذارید لحظه دستآزاد چیزی را جا بگذارد.
اکنون، بیایید روی چند نکته کاربردی برای ساختن تجربههای همزمان با Gemini Nano 4 و ML Kit که از ساختن این نسخه نمایشی یاد گرفتیم تمرکز کنیم.
نکته ۱، تأخیر همزمان را با طراحی دوگذر اولویتبندی کن.
در عینک صوتی، وقتی میپرسید «این چیست؟»
هر لحظه سکوت قابلتوجه است.
در مثال «معلم زبان واقعیت گسترده»، برای هر نوبت تعامل کاربر با مدل به دو چیز نیاز داریم: یک پاسخ گفتاری فوری و همچنین یک کارت واژگان ساختاریافته برای ذخیره در برنامه.
اگر در یک مرحله دو چیز را باهم از مدل بخواهیم، هر نشانهای که برای تولید دادههای پسزمینه صرف میشود، اولین جمله گفتاری را بهتأخیر میاندازد.
بنابراین، آنچه را کاربر درحالحاضر نیاز دارد از آنچه برنامه چند ثانیه بعد نیاز دارد جدا میکنیم.
بنابراین هر نوبت را به دو پاس تقسیم کردیم.
در «گذر ۱»، قاب دوربین و سؤال را فقط برای پاسخ گفتاری جاریسازی میکنیم و بهمحض تکمیل جمله اول، نوشتار به گفتار را راهاندازی میکنیم.
و چون آن TPU یا NPU تولید آن پاسخ کوتاه را خیلی قبلاز اینکه نوشتار به گفتار خواندن با صدای بلند را تمام کند به پایان میرساند، درواقع یک پنجره چندثانیهای بهدست میآوریم که کاربر هنوز درحال گوش دادن است و تراشه ازقبل بیکار نشسته است.
و ازآنجاییکه «گذر ۱» قبلاً شیء را در پاسخ نوشتاری خود شناسایی کرده است، «گذر ۲» حتی به ورودی تصویر نیاز ندارد.
ما فقط پاسخ «گذر ۱» را به آن تراشه غیرفعال برمیگردانیم تا کارت ساختاریافته را قبلاز اینکه معلم حتی صحبتش را تمام کند استخراج و ذخیره کنیم.
حالا بیایید روی «مجوز ۱» بزرگنمایی کنیم که من را به نکته دوم میرساند.
هنگام جاریسازی، از برچسبهای XML سبک استفاده کنید.
چون «گذر ۱» مستقیماً به نوشتار به گفتار جاریسازی میشود، نباید شیء JSON سنگین اولین کلمه گفتاری شما را نگه دارد.
درعوض، نوشتار سادهای میخواهید که بتواند فوراً با برچسبهای سبک و درونخطی صحبت کند، هر زمان که برنامه نیاز به اقدام در میان جریان داشته باشد.
پس بگذارید مشکلی را که ما را به اینجا رساند به شما نشان دهم.
آن اسباببازی چوبی فنجان و توپ را در غرفه بازار که گفتم بهخاطر دارید؟
در زبان ژاپنی به آن «کنداما» میگویند.
در همان ابتدا، مدل آن را از قاب دوربین تشخیص داد و پاسخ درست را برگرداند: «این کنداما است، یک اسباببازی چوبی.»
بنابراین ما آن جمله کامل را به جلسه نوشتار به گفتار انگلیسی دادیم و این چیزی است که حاصل شد.
این کن-می است، یک اسباببازی چوبی.
بهنظر درست نمیآید.
بهجای آن، این جمله باید اینگونه باشد…
این کنداما است، یک اسباببازی چوبی.
پس چرا این اتفاق افتاد؟
کنداما با هیراگانا، «کن»، و نویسه کانجی، «داما» نوشته شده است.
و موتور نوشتار به گفتار انگلیسی، هیراگانای آوایی مانند «کن» را بهخوبی تلفظ میکند، اما وقتی به بخش کانجی میرسد کاملاً ازهم میپاشد و «داما» را به چیزی شبیه به «می» تبدیل میکند.
و اگر منطقه زبانی را تغییر ندهید، این اتفاق درواقع در بسیاری از زبانهای مختلف به روشهای مختلف رخ میدهد.
برای مثال، در زبان اسپانیایی، ممکن است بشنوید…
Suh-BOH-luh…
وقتی واقعاً منتظر هستید…
Cebolla.
و بهطور مشابه، در زبان فرانسوی، ممکن است بشنوید…
PAIN oh chock-uh-lat…
وقتی واقعاً انتظار دارید…
نان شکلاتی.
بله، جلسه نوشتار به گفتار معمولاً برای یک زبان در هر زمان طراحی شده است، اما معلم زبان باید در هر جمله کد را تغییر دهد.
بنابراین، در مرحله ۱، از مدل میخواهیم که هر کلمه یا عبارت زبان مقصد را درحین جاریسازی در برچسب XML مقصد قرار دهد.
و وقتی بخشها میرسند، مشتری ما براساس هدف تقسیم میکند، هر بخش را به منطقه زبانی «نوشتار به گفتار» مربوطه هدایت میکند، که میتواند ژاپنی، اسپانیایی، فرانسوی، یا هر زبانی باشد که درحال یادگیری آن هستید، و سپس آنها را بهطور یکپارچه پخش میکند، لهجه را در وسط جمله تغییر میدهد، تا کلماتی مانند کنداما درست تلفظ شوند.
بنابراین، نکته مهم در اینجا این است که مدل ازقبل میداند کدام واژگان به کدام زبان تعلق دارد، کدام کلمات به کدام زبان تعلق دارد، و اینکه مدل درحین جاریسازی، بازههای هدف را بهصورت درونخطی برچسبگذاری کند، بسیار تمیزتر از این است که خودتان بعداً کل این فرایند را پسپردازش کنید.
و این هم گذر ۱ در کد.
// 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>"در بالا، از پیامواره استفاده میکنیم تا به مدل دستور دهیم در یک تا سه جمله داخل برچسبهای پاسخ جواب دهد و سپس هر کلمه یا جمله نمونه ژاپنی را داخل برچسبهای هدف قرار دهد.
سپس دوربین ImagePart و پیامواره TextPart را به generateContentStream ML Kit منتقل میکنیم و با رسیدن هر جمله، بخشهای هدف را به «نوشتار به گفتار» تقسیم میکنیم.
و این من را به مرحله ۲ و نکته سوم میرساند.
درحالیکه استفاده از برچسبهای XML سبک برای جاریسازی صوتی زنده عالی است، از برونداد ساختاریافته استفاده کنید که برای برگرداندن یک شیء تایپشده تمیز هنگام ذخیره دادهها در برنامهتان ساخته شده است.
چون «معلم زبان واقعیت گسترده» ما مکالمهای است، کاربران میتوانند سؤالات تعقیبی هم بپرسند که در آنها کلمه جدیدی آموزش داده نمیشود.
و وقتی کلمهای آموزش داده میشود، پایگاه داده برای ایجاد کارتهای واژگان به فیلدهای واژهنامه تمیز نیاز دارد.
بنابراین بهجای نوشتن عبارتهای باقاعده شکننده برای تجزیه کردن ترانویسی مرحله اول، اجازه میدهیم مرحله دوم شیء ایمن ازنظر نوع را در پسزمینه استخراج کند.
و برای اعمال طرحواره در مرحله ۲، از میانای برنامهسازی کاربردی خروجی ساختاریافته 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 حاشیهنویسی کنید، درست مانند مثال کلاس کارت واژگان ما در اینجا.
و با پیچیدن فیلد کارت قابلتهی، پاس ۲ یک کارت پرشده را هنگام آموزش کلمه جدید برمیگرداند، یا در نوبتهای صرفاً مکالمهای مقدار تهی برمیگرداند.
و این هم مجوز ۲ در کد.
// 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": "けん玉をください。", ...}در بالا، نوشتار پاسخ جاریشده از مرحله ۱ را میگیریم، نیازی به تصویر نیست، و پیامواره استخراج را میسازیم.
سپس، درحالیکه کاربر هنوز درحال گوش دادن به «نوشتار به گفتار» است، پیامواره و کلاس پوشاننده خود را به generateTypedContentRequest منتقل میکنیم و یک کارت واژگان تمیز و بدون برچسب استخراج میکنیم، یا اگر کلمهای آموزش داده نشده باشد، مقدار تهی برمیگردانیم.
بسیار خوب.
ما چیزهای زیادی را پوشش دادیم.
در این لحظه، این فکر منطقی است که بگویید: «عالی است، یک چیز دیگر که روی صحنه زیبا بهنظر میرسد اما من هرگز آن را لمس نخواهم کرد.»
این یکی نه!
«معلم زبان XR» در برنامه Google AI Edge Gallery درکنار دیگر تجربههای هوش مصنوعی دروندستگاهی ارائه میشود.
میتوانید آن را امروز بارگیری کنید، در تلفن خود قرار دهید، و به هر چیزی که میخواهید یاد بگیرید چگونه آن را به زبان دیگری بگویید، اشاره کنید.
و اگر میخواهید بدانید چگونه ساخته شده است، کل این پیادهسازی در GitHub بهصورت متنباز دردسترس است.
و نگران نباشید اگر همه کاربران شما هنوز عینک صوتی یا نمایشگردار یا دستگاههای پرچمدار Android پشتیبانیشده ندارند.
پایگاه کد ما همچنین نمونههایی از نحوه تطبیق همین تجربه برای تجربه فقط تلفن همراه را به شما نشان میدهد.
و اگر میخواهید به پلاتفرمهای دیگر هم دسترسی داشته باشید، کد برنامه Google AI Edge Gallery به شما نشان میدهد که چگونه این ویژگی را بااستفاده از زمان اجرای Google LiteRT و MediaPipe Tasks API بسازید.
یا اگر میخواهید مدلهای زبانی یا صوتی متنباز دیگری را به دستگاه بیاورید، میتوانید نحوه انجام این کار را از کد ببینید.
بنابراین، «معلم زبان واقعیت گسترده» را متنباز کردیم تا نمونهسازی را برای شما آسانتر کنیم.
نماینده موردنظرتان را به مخزن GitHub ما هدایت کنید تا این تجربه زنده، دروندستگاهی، و مبتنی بر هوش مصنوعی را به چیز دیگری تبدیل کنید.
این مدل میتواند راهنمای موزه باشد، یا شاید دستیار آشپزی که به پیشخوان شما نگاه میکند، راهنمای گامبهگام تعمیر، راوی دسترسپذیری، یا ابزار بازرسی میدانی باشد.
مدل، زمان اجرا، پیادهسازی متنباز، و ابزارها همگی اکنون دردسترس هستند تا بتوانید برای عرضه افزاردید هوشمند در پاییز امسال آماده شوید.
دوست داریم ببینیم با آن چه میسازید.
برای جمعبندی، امروز نمونههای قدرتمندی از آنچه با مدلهای هوش مصنوعی که بهطور یکپارچه در دستگاه اجرا میشوند امکانپذیر است نشان دادیم.
و در اینجا چند روش برای شروع کار آمده است.
ابتدا، منابع روی صفحه را بگیرید.
برنامه «گالری AI Edge» را بارگیری کنید تا نسخه نمایشی را امتحان کنید و کد مرجع را در GitHub بررسی کنید.
و رمزینههای پاسخسریع را اسکن کنید تا به Android XR SDK، میاناهای برنامهسازی کاربردی ML Kit، و پیشنهادهای Google AI Edge دسترسی پیدا کنید.
دوم، همین امروز آزمایش را مستقیماً در تلفن شروع کنید.
موارد زیادی را پوشش دادیم که همینحالا هم میتوانند با تجربه اولویت تلفن همراه ساخته شوند.
و سوم، به برنامه تلفن همراهی که درحالحاضر عرضه میکنید نگاه کنید و از خودتان بپرسید کدام ویژگیهای اصلی بهتر است بدون نیاز به دست کار کنند؟
برای اینکه آن تجربه را به عینک هم بیاورید، کافی است «فعالیت پیشبینیشده» را به 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": "けん玉をください。", ...}