[Unity3D] Naver CLOVA Speech Recognition API 사용하기

zmin·2022년 2월 26일
1
post-thumbnail

Unity 2020.3.23f1

이전까지 STT API로 IBM Watson를 사용하여 프로젝트를 진행 중이었는데 갑작스레 난관에 부딪혔다.
한국어를 지원하지만 제대로 인식이 되지 않았고, 설상가상으로 계정이 정지가 되어 클라우드에 로그인 조차 할 수 없게 되었다.(이유도 알려주지 않았는데 문의를 하려면 로그인을 해야한다...) 그래서 다른 API를 찾아보게 되었고 좀 더 정확한 한국어 인식을 위하여 멘토님께서 추천해주신 CLOVA를 사용하게 되었다. 결제 수단을 등록하면 10만 크레딧을 이벤트로 주기 때문에 비용문제 또한 해결할 수 있어 프로젝트에 적합하다고 판단했다.

  • 한국어 인식 정확도가 높음
  • 단순히 소리를 인식하는 것이 아니라 의미있는 말을 출력으로 보내줌
  • 이벤트로 제공하는 10만 크레딧이 유용함

제품 등록 및 인증키🔑

우선 Naver Cloud Platform에 로그인한 뒤 콘솔페이지로 들어간다.
AI·NAVER·API - Application에서 Application 이름을 적어주고 내가 필요한 서비스를 체크하여 등록할 수 있다. 이 글에선 CLOVA Speech Recognition(CSR)을 사용한다.
그리고 아래로 내리면 서비스 환경 등록이라는 것을 해야 하는데, 우리 프로젝트는 Android 앱 개발이기 때문에 해당 위치에 패키지 이름을 적어주었다. 유니티에서는 다음 위치에서 확인할 수 있다.
Project Setting - Player(Player Setting) - Other Setting - Idendification - Package Name

이후 화면에서 인증 정보를 눌러 Client ID와 Client Secret을 를 확인할 수 있다.

REST API를 위한 스크립트 작성📃

참고자료

https://api.ncloud-docs.com/docs/ai-naver-clovaspeechrecognition-stt
https://github.com/deadlyfingers/UnityWav/blob/master/WavUtility.cs

우선 CSR에서 Android SDK를 제공하고 있지만 Unity 개발과 형태가 다르다고 판단하여 REST API를 이용하는 스크립트를 작성하였다.

과정은 다음과 같다.
녹음 - 변환 - STT요청 - 결과값처리

Microphone을 통한 음성 녹음

유니티에선 장치의 마이크에 접근할 수 있는 Microphone 클래스를 제공한다. 공식 문서
여기서는 버튼을 누르기 시작할 때 녹음을 시작하고 버튼에서 손을 떼어낼 때 녹음을 끝내도록 코드를 작성하였다.

private string _microphoneID = null;
private AudioClip _recording = null;
private int _recordingLengthSec = 15;
private int _recordingHZ = 22050;

private void Start()
{
    _microphoneID = Microphone.devices[0];
}

// 버튼을 OnPointerDown 할 때 호출
public void startRecording()
{
	Debug.Log("start recording");
	_recording = Microphone.Start(_microphoneID, false, _recordingLengthSec, _recordingHZ);
}
// 버튼을 OnPointerUp 할 때 호출
public void stopRecording()
{
	if (Microphone.IsRecording(_microphoneID))
	{
		Microphone.End(_microphoneID);

		Debug.Log("stop recording");
		if (_recording == null)
		{
			Debug.LogError("nothing recorded");
			return;
		}
		// audio clip to byte array
		byte[] byteData = getByteFromAudioClip(_recording);

		// 녹음된 audioclip api 서버로 보냄
		StartCoroutine(PostVoice(url, byteData));
	}
	return;
}

이런 코드를 통에 사용자의 음성을 AudioClip변수인 _recording에 저장할 수 있다.

Audio Clip to Byte Array

공식 가이드에서는 오디오 파일을 Path로 불러와서 이를 변환하는 예제를 제공한다. 그러나 우리는 해당 음성을 따로 보관할 필요도 없고 저장했다 불러오고 다시 삭제하는 것은 비효율적인 작업이라고 생각하여 여기를 참고하여 Audio Clip을 byte array로 변환할 수 있도록 작성하였다. 아래는 작성 코드의 일부이다.

private byte[] getByteFromAudioClip(AudioClip audioClip)
{
    MemoryStream stream = new MemoryStream();
    const int headerSize = 44;
    ushort bitDepth = 16;

    int fileSize = audioClip.samples * BlockSize_16Bit + headerSize;

    // audio clip의 정보들을 file stream에 추가(링크 참고 함수 선언)
    WriteFileHeader(ref stream, fileSize);
    WriteFileFormat(ref stream, audioClip.channels, audioClip.frequency, bitDepth);
    WriteFileData(ref stream, audioClip, bitDepth);
    
    // stream을 array형태로 바꿈
    byte[] bytes = stream.ToArray();

    return bytes;
}

UnityWebRequest

공식 가이드에서 나와 있듯이 메세지에 다음 요소들을 담아서 보내면 된다.
유니티에서 제공하는 UnityWebRequest라는 클래스를 사용하여 작성하였다.
아래와 같은 코드를 통해 요청 메세지를 작성하고 전송할 수 있다.

// 받아온 값에 간편하게 접근하기 위한 JSON 선언
[Serializable]
public class VoiceRecognize
{
    public string text;
}

// 사용할 언어(Kor)를 맨 뒤에 붙임
url = "https://naveropenapi.apigw.ntruss.com/recog/v1/stt?lang=Kor"

private IEnumerator PostVoice(string url, byte[] data)
{
	// request 생성
    WWWForm form = new WWWForm();
    UnityWebRequest request = UnityWebRequest.Post(url, form);
    
    // 요청 헤더 설정
    request.SetRequestHeader("X-NCP-APIGW-API-KEY-ID", YOUR_CLIENT_ID);
    request.SetRequestHeader("X-NCP-APIGW-API-KEY", YOUR_CLIENT_SECRET);
    request.SetRequestHeader("Content-Type", "application/octet-stream");
    
    // 바디에 처리과정을 거친 Audio Clip data를 실어줌
    request.uploadHandler = new UploadHandlerRaw(data);
    
    // 요청을 보낸 후 response를 받을 때까지 대기
    yield return request.SendWebRequest();
    
    // 만약 response가 비어있다면 error
    if (request == null)
    {
        Debug.LogError(request.error);
    }
    else
    {
        // json 형태로 받음 {"text":"인식결과"}
        string message = request.downloadHandler.text;
        VoiceRecognize voiceRecognize = JsonUtility.FromJson<VoiceRecognize>(message);

        Debug.Log("Voice Server responded: " + voiceRecognize.text);
        // Voice Server responded: 인식결과
    }
}

YOUR_CLIENT_IDYOUR_CLIENT_SECRET 위치에 위에서 확인한 인증키를 넣어주면 된다.
해당 POST 요청을 보낸 후 기다렸다가 response를 받아야하기 때문에 Coroutine을 사용해주었다.

결과👍


인식이 아주 잘 된다! 짧은 말이라도 한 번 요청하면 15초의 가격으로 처리되지만 가장 처음 말했다시피 아직은 크게 비용 부담은 없다.

핸드폰에서도 잘 실행되는 것을 확인할 수 있다.

profile
308 Permanent Redirect

1개의 댓글

comment-user-thumbnail
2022년 11월 20일

Audio Clip to Byte Array 설명에 있는 링크가 잘못된거같아요 ㅜ

답글 달기