관리 메뉴

hyeals study

구글 TTS API를 안드로이드에서 써보기!(2??) 본문

안드로이드

구글 TTS API를 안드로이드에서 써보기!(2??)

hyeals 2021. 2. 14. 23:10

지난 포스팅에서 구글 TTS API를 안드로이드에 써보는 걸 시도해봤는데 여기에 RxJava를 이용해서 리액티브한 요소를 한 번 집어넣어보면 어떨까해서 소소한 2탄을 준비했다. ㅎㅎ

 

목표는 EditText의 입력을 RxTextView를 이용해서 Observable로 만들어서 입력에 대한 변화를 관찰하게 만든다. 그 다음 이걸  Disposable과 함께 사용해서 좀 더 리액티브하게, 간단한 코드로 사용할 수 있도록 하는 것이다!

 

*RxTextView는 EditText를 Observable로 만들어준다.(RxBinding라이브러리에 포함되어 있다.)

 

2021/02/10 - [안드로이드] - 구글 TTS API를 안드로이드에서 써보기!

 

구글 TTS API를 안드로이드에서 써보기!

TTS란? Text To Speech의 약자로 텍스트를 음성으로 출력해주는 기술이다. (안드로이드에서는 무료로 제공해준다. 구글 TTS를 내장 API로 지원해주고 있다!) 물론 반대인 STT(Speech To Text)로 지원해준다.

hyeals.tistory.com

 

일단 gradle에 아래와 같이 라이브러리들을 추가해줘야한다.

	//RxAndroid와 RxJava , RxKotlin 추가
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation 'io.reactivex.rxjava3:rxjava:3.0.6'
    implementation 'io.reactivex.rxjava3:rxkotlin:3.0.0'

    // 필요한 API를 찾아서 추가
    implementation 'com.jakewharton.rxbinding4:rxbinding:4.0.0'

 

그리고 activity_main.xml에 TextView를 추가해줘야 한다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:inputType="textPersonName" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"></TextView>

    <Button
        android:id="@+id/button"
        android:layout_gravity="center_horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="음성 출력" />
</LinearLayout>

 

요정도 추가해주고 저번 MainActivity.java코드를 아래와 같이 조금 수정해준다.

package com.example.tts_test;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Build;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.jakewharton.rxbinding4.InitialValueObservable;
import com.jakewharton.rxbinding4.widget.RxTextView;

import java.util.Locale;

import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.observers.DisposableObserver;
import io.reactivex.rxjava3.subjects.BehaviorSubject;

public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {

    private Disposable disposable;
    private TextToSpeech tts;
    private Button speak_out;
    private EditText input_text;
    private TextView result_text;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tts = new TextToSpeech(this, this);
        speak_out = findViewById(R.id.button);
        input_text = findViewById(R.id.editText);
        result_text = findViewById(R.id.textView);

        observeTextWatcher(input_text);

        speak_out.setOnClickListener(new View.OnClickListener(){
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) // LOLLIPOP이상 버전에서만 실행 가능
            @Override
            public void onClick(View v){
                speakOut();
            }
        });
    }

    public void observeTextWatcher(TextView tv){
        Observable ob = RxTextView.textChanges(tv);
        disposable = ob.subscribe(text -> result_text.setText("INPUT: " + text.toString()));
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void speakOut(){
        CharSequence text = input_text.getText();
        tts.setPitch((float)0.6); // 음성 톤 높이 지정
        tts.setSpeechRate((float)0.1); // 음성 속도 지정

        // 첫 번째 매개변수: 음성 출력을 할 텍스트
        // 두 번째 매개변수: 1. TextToSpeech.QUEUE_FLUSH - 진행중인 음성 출력을 끊고 이번 TTS의 음성 출력
        //                 2. TextToSpeech.QUEUE_ADD - 진행중인 음성 출력이 끝난 후에 이번 TTS의 음성 출력
        tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, "id1");
    }

    @Override
    public void onDestroy() {
        if(tts!=null){ // 사용한 TTS객체 제거
            tts.stop();
            tts.shutdown();
        }
        disposable.dispose();
        super.onDestroy();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onInit(int status) { // OnInitListener를 통해서 TTS 초기화
        if(status == TextToSpeech.SUCCESS){
            int result = tts.setLanguage(Locale.KOREA); // TTS언어 한국어로 설정

            if(result == TextToSpeech.LANG_NOT_SUPPORTED || result == TextToSpeech.LANG_MISSING_DATA){
                Log.e("TTS", "This Language is not supported");
            }else{
                speak_out.setEnabled(true);
                speakOut();
            }
        }else{
            Log.e("TTS", "Initialization Failed!");
        }
    }
}

 

이렇게 하면 resultText에 EditText에 입력한 문자열?이 결과로 출력된다. 입력값이 변화함에 따라 함께 계속 바뀐다!

이런 방식은 UI이벤트가 Rxjava로 인해 data stream형태로 출력되는것을 나타낸다!

 

아마 더 복잡한 내용을 다룰 수 있을 것 같다. (공부하면 할수록 헷갈리는 부분이 많아서 책 한 권사서 정리하는것도 나쁘지 않을 것 같다 ㅠ)

 

* Disposable을 선언한 이유: 메모리 누수를 방지하기 위해서 Destroy부분에서 꼭 .dispose()를 이용해서 구독을 끝내야 한다.

 

 

아래는 실행화면이다.

 

 

'안드로이드' 카테고리의 다른 글

구글 TTS API를 안드로이드에서 써보기!  (1) 2021.02.10
Rxjava를 안드로이드에 적용시켜보기!  (0) 2021.02.08
RxJava 사용해보기  (0) 2021.02.07
RxJava???  (0) 2021.02.06
웹툰 바로가기 앱  (0) 2020.05.24
Comments