Browsing Tag

papago translation

NCLOUD

[NCLOUD] CLOVA Voice로 창조하는 오디오 마스터피스

안녕하세요 MANVSCLOUD 김수현입니다.

오늘날의 기술 세계에서 음성 합성은 단순히 ‘텍스트를 음성으로 변환하는 것’을 넘어서 인간의 음성을 모방하고 감정과 맥락을 이해하는 놀라운 수준에 도달했습니다. 이러한 혁신의 최전선에는 CLOVA Voice가 있습니다. 이 글에서는 CLOVA Voice 서비스의 핵심 기능과 NeuVis(Neural Voice Synthesis) 기술이 어떻게 이 서비스를 독특하게 만드는지 탐구해보겠습니다.


CLOVA Voice

CLOVA Voice는 Naver Cloud Platform의 AI 서비스 중 하나로 고도의 음성 인식 및 합성 기능을 제공합니다. 더욱 자연스럽고 맥락을 이해하며 감정을 표현할 수 있는 음성 인터페이스의 가능성을 열어주는 고품질 음성 합성 서비스인데요. 기업이나 개발자들은 CLOVA Voice를 이용하여 애플리케이션, 서비스 또는 제품에 자연스러운 음성 인터페이스를 통합할 수 있습니다.

CLOVA Voice의 핵심은 NeuVis(Neural Voice Synthesis), 즉 신경망 음성 합성 기술에 있습니다. 이 기술은 딥러닝을 기반으로 실제 인간의 음성과 거의 구분이 불가능한 수준의 자연스러운 음성을 생성합니다. 인공적인 ‘로봇’ 같은 음성이 아니라 감정과 억양을 포함한 인간과 같은 음성을 통해 고도의 자연스러움을 느낄 수 있고 문장의 맥락을 이해하고 그에 맞는 억양과 강조를 적용합니다. 또한 다양한 목소리와 언어를 지원하고 있어 광범위한 사용자 요구에 부응합니다.

CLOVA Voice를 사용한다면 자동 응답 시스템(ARS)이나 챗봇에서 인간처럼 자연스러운 음성을 제공하여 고객 경험을 향상시키거나 오디오북, 팟캐스트, 비디오 콘텐츠에 다양한 목소리와 언어로 음성을 제공하거나 다양한 언어로 교육 자료를 제공하는 학습 효과까지 기대할 수 있을 것입니다.


Hands On Lab #1

그렇다면 CLOVA Voice는 어떻게 사용할 수 있을까요?
먼저 CLOVA Voice를 사용하기 위해서는 API 가이드를 잘 읽어 보아야합니다.

CLOVA Voice는 음성 합성에 사용할 목소리가 다양하게 존재하고 음성 볼륨, 음성 속도, 음성 피치, 음성 감정, 감정의 강도, 음성 포맷, 샘플링 레이트, 음색, 끝음 처리를 조절할 수 있기에 잘 읽어보고 원하는 결과를 얻을 수 있도록 해야합니다.

아래는 CLOVA Voice의 예시입니다.

import os
import sys
import urllib.request

client_id = "[YOUR_CLIENT_ID]"
client_secret = "[YOUR_CLIENT_SECRET]"

encText = urllib.parse.quote("안녕하세요 오늘은 CLOVA Voice에 대해 알아보겠습니다.")

data = "speaker=vgoeun&volume=0&speed=0&pitch=0&format=mp3&text=" + encText;
url = "https://naveropenapi.apigw.ntruss.com/tts-premium/v1/tts"

request = urllib.request.Request(url)
request.add_header("X-NCP-APIGW-API-KEY-ID",client_id)
request.add_header("X-NCP-APIGW-API-KEY",client_secret)

response = urllib.request.urlopen(request, data=data.encode('utf-8'))
rescode = response.getcode()

if(rescode==200):
    response_body = response.read()
    with open('voice.mp3', 'wb') as f:
        f.write(response_body)
else:
    print("Error Code:" + rescode)

[YOUR_CLIENT_ID]와 [YOUR_CLIENT_SECRET] 부분은 CLOVA Voice의 [인증 정보]를 클릭하여 Client ID와 Client Secret을 복사하여 추가해줍니다.
(반드시 외부에 유출이되지 않도록 유의하세요!)

코드에 나와있는 “안녕하세요 오늘은 CLOVA Voice에 대해 알아보겠습니다.” 텍스트를 voice.mp3 파일로 바꾸어 저장하는 코드입니다.

실행 후 저장된 voice.mp3 파일을 들어볼까요?

어떤가요?
CLOVA Voice 사용하는 방법은 크게 어렵지 않습니다.
CLOVA Voice의 다양한 목소리를 들어보고 피치, 속도, 감정을 조절해보시고 싶으신가요?
네이버 클라우드 CLOVA Voice 서비스 소개 페이지에서 경험해보실 수 있습니다.


Hands On Lab #2

CLOVA Voice 사용 방법에 대해 간단하게 알아보았습니다.
지난 포스팅에서 우리는 CLOVA Speech를 이용하여 음성을 텍스트로 변환 시킨 파일을 Papago Translation을 이용하여 자동 번역하도록 하였는데요.

이제 CLOVA Voice를 이용하여 번역된 텍스트를 다시 음성으로 변환하는 과정을 진행해보겠습니다.

import hashlib
import hmac
import datetime
import requests
import urllib.parse
import xml.etree.ElementTree as ET
import json
import os
import sys
import urllib.request

def get_hash(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()

def create_signed_headers(headers):
    signed_headers = []
    for k in sorted(headers):
        signed_headers.append('%s;' % k)
    return ''.join(signed_headers)[:-1]

def create_standardized_headers(headers):
    signed_headers = []
    for k in sorted(headers):
        signed_headers.append('%s:%s\n' % (k, headers[k]))
    return ''.join(signed_headers)

def create_standardized_query_parameters(request_parameters):
    standardized_query_parameters = []
    if request_parameters:
        for k in sorted(request_parameters):
            standardized_query_parameters.append('%s=%s' % (k, urllib.parse.quote(request_parameters[k], safe='')))
        return '&'.join(standardized_query_parameters)
    else:
        return ''

class ObjectStorageSample:
    def __init__(self, access_key, secret_key):
        self.region = 'kr-standard'
        self.endpoint = 'https://kr.object.ncloudstorage.com'
        self.host = 'kr.object.ncloudstorage.com'
        self.access_key = access_key
        self.secret_key = secret_key
        self.payload_hash = 'UNSIGNED-PAYLOAD'
        self.hashing_algorithm = 'AWS4-HMAC-SHA256'
        self.service_name = 's3'
        self.request_type = 'aws4_request'
        self.time_format = '%Y%m%dT%H%M%SZ'
        self.date_format = '%Y%m%d'

    def _create_credential_scope(self, date_stamp):
        return date_stamp + '/' + self.region + '/' + self.service_name + '/' + self.request_type

    def _create_canonical_request(self, http_method, request_path, request_parameters, headers):
        standardized_query_parameters = create_standardized_query_parameters(request_parameters)
        standardized_headers = create_standardized_headers(headers)
        signed_headers = create_signed_headers(headers)

        canonical_request = (http_method + '\n' +
                             request_path + '\n' +
                             standardized_query_parameters + '\n' +
                             standardized_headers + '\n' +
                             signed_headers + '\n' +
                             self.payload_hash)

        return canonical_request

    def _create_string_to_sign(self, time_stamp, credential_scope, canonical_request):
        string_to_sign = (self.hashing_algorithm + '\n' +
                          time_stamp + '\n' +
                          credential_scope + '\n' +
                          hashlib.sha256(canonical_request.encode('utf-8')).hexdigest())

        return string_to_sign

    def _create_signature_key(self, date_stamp):
        key_date = get_hash(('AWS4' + self.secret_key).encode('utf-8'), date_stamp)
        key_string = get_hash(key_date, self.region)
        key_service = get_hash(key_string, self.service_name)
        key_signing = get_hash(key_service, self.request_type)
        return key_signing

    def _create_authorization_header(self, headers, signature_key, string_to_sign, credential_scope):
        signed_headers = create_signed_headers(headers)
        signature = hmac.new(signature_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()

        return (self.hashing_algorithm + ' ' +
                'Credential=' + self.access_key + '/' + credential_scope + ', ' +
                'SignedHeaders=' + signed_headers + ', ' +
                'Signature=' + signature)


    def _sign(self, http_method, request_path, headers, time, request_parameters=None):
        time_stamp = time.strftime(self.time_format)
        date_stamp = time.strftime(self.date_format)

        credential_scope = self._create_credential_scope(date_stamp)
        canonical_request = self._create_canonical_request(http_method, request_path, request_parameters, headers)
        string_to_sign = self._create_string_to_sign(time_stamp, credential_scope, canonical_request)
        signature_key = self._create_signature_key(date_stamp)

        headers['authorization'] = self._create_authorization_header(headers, signature_key, string_to_sign, credential_scope)


    def list_objects(self, bucket_name, request_parameters=None):
        http_method = 'GET'

        time = datetime.datetime.utcnow()
        time_stamp = time.strftime(self.time_format)

        headers = {'x-amz-date': time_stamp,
                   'x-amz-content-sha256': self.payload_hash,
                   'host': self.host}

        request_path = '/%s' % bucket_name

        self._sign(http_method, request_path, headers, time, request_parameters)

        request_url = self.endpoint + request_path
        r = requests.get(request_url, headers=headers, params=request_parameters)

        if r.status_code == 200:
            root = ET.fromstring(r.content)
            objects = []

            for content in root.findall('{http://s3.amazonaws.com/doc/2006-03-01/}Contents'):
                key = content.find('{http://s3.amazonaws.com/doc/2006-03-01/}Key').text
                if key.endswith('.json'):
                    last_modified = content.find('{http://s3.amazonaws.com/doc/2006-03-01/}LastModified').text
                    objects.append({'Key': key, 'LastModified': last_modified})

            objects.sort(key=lambda x: x['LastModified'], reverse=True)
            return objects

        else:
            return []

    def read_json_object(self, bucket_name, object_name):
        http_method = 'GET'

        time = datetime.datetime.utcnow()
        time_stamp = time.strftime(self.time_format)

        headers = {'x-amz-date': time_stamp,
                   'x-amz-content-sha256': self.payload_hash,
                   'host': self.host}

        request_path = '/%s/%s' % (bucket_name, object_name)

        self._sign(http_method, request_path, headers, time)

        request_url = self.endpoint + request_path
        r = requests.get(request_url, headers=headers)

        if r.status_code == 200:
            content = json.loads(r.content.decode('utf-8'))

            return content.get('text', '')


if __name__ == '__main__':

    storage_sample = ObjectStorageSample('[ACCESS_KEY_ID]', '[SECRET_KEY]')

    objects = storage_sample.list_objects('[BUCKET_NAME]', {'max-keys': '10'})
    if objects:
        most_recent_object = objects[0]
        text_to_translate = storage_sample.read_json_object('[BUCKET_NAME]', most_recent_object['Key'])

        # 네이버 Papago Text Translation API 사용
        papago_client_id = "[PAPAGO_CLIENT_ID]"
        papago_client_secret = "[PAPAGO_CLIENT_SECRET]"

        encText = urllib.parse.quote(text_to_translate)
        data = "source=ko&target=en&text=" + encText
        url = "https://naveropenapi.apigw.ntruss.com/nmt/v1/translation"
        request = urllib.request.Request(url)
        request.add_header("X-NCP-APIGW-API-KEY-ID", papago_client_id)
        request.add_header("X-NCP-APIGW-API-KEY", papago_client_secret)

        response = urllib.request.urlopen(request, data=data.encode("utf-8"))
        rescode = response.getcode()

        if rescode == 200:
            response_body = response.read()
            translated_text = json.loads(response_body.decode('utf-8'))['message']['result']['translatedText']

            # CLOVA Voice API 사용
            clova_client_id = "[VOICE_CLIENT_ID]"
            clova_client_secret = "[VOICE_CLIENT_SECRET]"

            encText = urllib.parse.quote(translated_text)
            data = "speaker=matt&volume=0&speed=0&pitch=0&format=mp3&text=" + encText
            url = "https://naveropenapi.apigw.ntruss.com/tts-premium/v1/tts"
            request = urllib.request.Request(url)
            request.add_header("X-NCP-APIGW-API-KEY-ID", clova_client_id)
            request.add_header("X-NCP-APIGW-API-KEY", clova_client_secret)

            response = urllib.request.urlopen(request, data=data.encode('utf-8'))
            rescode = response.getcode()

            if rescode == 200:
                response_body = response.read()
                timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
                voice_directory = '/root/ai/voice'
                if not os.path.exists(voice_directory):
                    os.makedirs(voice_directory)
                file_name = f'voice-{timestamp}.mp3'
                file_path = os.path.join(voice_directory, file_name)

                with open(file_path, 'wb') as f:
                    f.write(response_body)

            else:
                print("Error in CLOVA Voice API:", rescode)
        else:
            print("Error in Papago Translation API:", rescode)

이전 “[NCLOUD] 언어의 장벽을 넘어 : PAPAGO TRANSLATION” 포스팅에서 번역된 텍스트를 CLOVA Voice를 이용하여 음성으로 변환하고 /root/ai/voice 경로에 voice-{timestamp}.mp3 형식의 이름으로 저장되도록 하였습니다.

원한다면 로컬에 저장하지않고 Object Storage에 저장되도록 할 수도 있습니다.

코드에 포함된 [ACCESS_KEY_ID], [SECRET_KEY], [BUCKET_NAME], [CLIENT_ID], [CLIENT_SECRET]는 각 항목에 맞게 수정이 필요합니다.

  • [ACCESS_KEY_ID] : Object Storage에 대한 권한을 보유한 Sub Account의 Access Key
  • [SECRET_KEY] : Object Storage에 대한 권한을 보유한 Sub Account의 Secret Key
  • [BUCKET_NAME] : CLOVA Speech를 사용하여 텍스트로 변환된 .json파일이 있는 Object Storage의 Bucket Name
  • [PAPAGO_CLIENT_ID] : Papago Translation의 Application Key 중 Client ID
  • [PAPAGO_CLIENT_SECRET] : Papago Translation의 Application Key 중 Client Secret
  • [VOICE_CLIENT_ID] : CLOVA Voice의 Application Key 중 Client ID
  • [VOICE_CLIENT_SECRET] : CLOVA Voice의 Application Key 중 Client Secret

지난번 포스팅에서 Papago Translation 서비스를 이용하여 얻은 텍스트 결과는 아래와 같았습니다.

Naver Cloud Platform is a public cloud service that contains technology that Naver has accumulated over the past 20 years. Naver has become the largest portal site in Korea, and Line Messenger's technology that has been loved and nurtured around the world can now be easily used through Naver's cloud platform. Naver's technology can be easily applied wherever the touch of Naver's cloud platform is needed, not only for large companies but also for the game industry, which bans education and finance from public institutions. Startups and students who need support are also growing together within the ecosystem of Naver's cloud platform. Then, in what way are customers attracted to the Naver Cloud platform. By obtaining the csa Star Gold certification, it has been recognized worldwide for its cloud technology, and by obtaining the csap certification, it is qualified to provide services to public institutions in Korea. In addition, we are responding quickly to a variety of issues with 24/7 customer support. Cloud service issues are directly related to your business, so you need close support As it is headquartered in Korea, support programs specialized in domestic business provide a differentiated customer experience from other companies. In addition, the service portfolio with all the necessary services for the business is also why Naver Cloud Platform is in the spotlight by many companies. From basic infrastructure products to api services such as map chatbot voice recognition and ai monitoring security products that incorporate Naver's technology, it has a diverse lineup of products needed for stable customer service. I'm helping you go around Naver. The real challenge for Naver's cloud platform is just beginning. We want to meet more businesses and grow with Naver. Hybrid clouds will also help you grow by meeting more businesses while directly providing the right data center environment for customers who cannot deploy the cloud due to regulations and policies. Naver Cloud Platform will be with you.

위 Python 코드를 실행한 후 얻은 .mp3 파일은 어떤 음성을 저희에게 들려줄까요?
먼저 코드 실행 시 아래 화면과 같이 저장이 되었습니다.

한번 들어보시죠.

텍스트가 음성으로 잘 번역된 것을 들으실 수 있습니다.

지금까지 “CLOVA AI : 한국에 특화된 인공지능”, “[NCLOUD] 언어의 장벽을 넘어 : PAPAGO TRANSLATION” 그리고 지금 읽고있는 “[NCLOUD] CLOVA Voice로 창조하는 오디오 마스터피스”까지 우리는 음성을 텍스트로 변경하고 변경된 텍스트를 번역하고 번역된 텍스트를 다시 음성으로 변경하였습니다.

이 모든 과정을 실시간으로 구현할 수 있다면 우리는 세계 각국의 다양한 언어를 배우지 않아도 전세계 사람들과 어려움없이 의사소통이 가능해질 것입니다.


Personal Comments

지금까지 CLOVA Voice에 대해서 그리고 CLOVA Voice를 어떻게 활용할 수 있는지 알아보았습니다.

CLOVA Voice의 응용 분야는 매우 다양합니다.
고객 서비스, 교육, 엔터테인먼트 등 다양한 분야에서 이 기술은 사용자 경험을 향상시키고 언어 장벽을 허물며 정보 접근성을 증대시키는 데 기여할 수 있습니다.
특히 실시간 음성 변환 및 번역 기능이 구현된다면 세계적인 의사소통의 장벽을 낮추는 데 큰 역할을 할 것입니다.

긴 글 읽어주셔서 감사합니다.