iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
文章読み上げの機能を利用するためには、AndroidManifest.xmlでの宣言が必要です。
<manifest>
<application>
</application>
<queries>
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
</intent>
</queries>
</manifest>
Jetpack Compose的なグローバルな変数を保持する方法はあると思いますが、TextToSpeechインスタンスはsetContentの外のメンバーとして保持しています。
class MainActivity : ComponentActivity(), TextToSpeech.OnInitListener {
private var textToSpeech: TextToSpeech? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.textToSpeech = TextToSpeech(this, this)
setContent {
TextToSpeechTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
Greeting(this.textToSpeech)
}
}
}
}
onInit()で初期化を
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
val locale = Locale.JAPAN
if (this.textToSpeech!!.isLanguageAvailable(locale) >= TextToSpeech.LANG_AVAILABLE) {
this.textToSpeech!!.language = Locale.JAPAN
}
// this.tts!!.speak("こんにちは", TextToSpeech.QUEUE_FLUSH, null, "utteranceId")
}
}
}
入力フィールドとボタンのコンテンツ。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Greeting(textToSpeech: TextToSpeech?, modifier: Modifier = Modifier) {
val textValue = rememberSaveable { mutableStateOf("文字列を入力してください。") }
Column {
TextField(
value = textValue.value,
onValueChange = { textValue.value = it },
label = { },
modifier = Modifier.padding(16.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Button(
onClick = {
if (textToSpeech != null) {
textToSpeech!!.speak(
textValue.value,
TextToSpeech.QUEUE_FLUSH,
null,
"utteranceId"
)
}
}
) {
Text("Say")
}
}
}
rememberSaveableで文章を永続的に保持し、入力フィールドの値をtextToSpeechに渡している。
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
TextToSpeechTheme {
Greeting(null)
}
}
以下のデバッグ出力を仕込んで、利用できるヴォイスをダンプしてみた。
this.textToSpeech!!.voices.forEach { voice -> Log.d("MainActivity", voice.toString()) }
量が多いので日本語関連のみ抜き出してみる。
Voice[Name: ja-jp-x-jab-network, locale: ja_JP, quality: 400, latency: 200, requiresNetwork: true, features: [networkTimeoutMs, networkRetriesCount]]
Voice[Name: ja-jp-x-htm-network, locale: ja_JP, quality: 400, latency: 200, requiresNetwork: true, features: [networkTimeoutMs, networkRetriesCount]]
Voice[Name: ja-jp-x-jad-network, locale: ja_JP, quality: 400, latency: 200, requiresNetwork: true, features: [networkTimeoutMs, networkRetriesCount]]
Voice[Name: ja-jp-x-jab-local, locale: ja_JP, quality: 400, latency: 200, requiresNetwork: false, features: [networkTimeoutMs, networkRetriesCount]]
Voice[Name: ja-jp-x-jad-local, locale: ja_JP, quality: 400, latency: 200, requiresNetwork: false, features: [networkTimeoutMs, networkRetriesCount]]
Voice[Name: ja-jp-x-jac-local, locale: ja_JP, quality: 400, latency: 200, requiresNetwork: false, features: [networkTimeoutMs, networkRetriesCount]]
Voice[Name: ja-jp-x-jac-network, locale: ja_JP, quality: 400, latency: 200, requiresNetwork: true, features: [networkTimeoutMs, networkRetriesCount]]
Voice[Name: ja-jp-x-htm-local, locale: ja_JP, quality: 400, latency: 200, requiresNetwork: false, features: [networkTimeoutMs, networkRetriesCount]]
Appleと異なり、声色毎という方向での充実ではなさそうですね。