文字转语音播报模块(一):阿里云nls服务使用示例

一、业务场景

最近笔者在业务中涉及到语音告警的模块,需要讲告警内容以文件或流形式返回给前端进行语音播报,具体的分析与处理如下

二、业务分析

首先告警内容提示信息这里做的处理是通过专门字段去存储、编辑,根据拟定好的代码逻辑判断是否触发语音告警操作,比较重要的是考虑用那种api实现文字转语音功能,这里笔者选用的是阿里云的nls语音服务。

三、解决方案

1、pom文件引入

<dependency>
    <groupId>com.alibaba.nls</groupId>
    <artifactId>nls-sdk-tts</artifactId>
    <version>2.2.1</version>
</dependency>

2、yml文件配置添加

#  nls语音配置
ali:
  nls:
  ##产品相关信息
    app-key: 
    access-key-id: 
    access-key-secret: 
  ##语音信息(声音类型、语速)
    voice: sijia
    pitch-rate: -68
    speech-rate: 0

3、实体类构建

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "ali.nls")
public class NlsProperties {

    private String appKey;

    private String accessKeyId;

    private String accessKeySecret;

    private String url;

    //发音人
    private String voice;
    //语调,范围是-500~500,可选,默认是0
    private Integer pitchRate;
    //语速,范围是-500~500,默认是0
    private Integer speechRate;

}

4、config类构建

import com.demo.NlsProperties;
import com.demo.NlsTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;

@Configuration
public class NlsAutoConfiguration {

    @Bean
    public NlsTemplate nlsTemplate(NlsProperties nlsProperties, StringRedisTemplate stringRedisTemplate){
        return new NlsTemplate(nlsProperties,stringRedisTemplate);
    }

}

5、服务类构建

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.nls.client.AccessToken;
import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.demo.NlsProperties;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.io.*;
import java.util.Date;

@Slf4j
@RequiredArgsConstructor
public class NlsTemplate {

    private final NlsProperties nlsProperties;

    private final StringRedisTemplate stringRedisTemplate;

    /**
     * HTTPS POST请求
     */
    public byte[] processPOSTRequest(String text) {
        /**
         * 设置HTTPS POST请求:
         * 1.使用HTTPS协议
         * 2.语音合成服务域名:nls-gateway-cn-shanghai.aliyuncs.com
         * 3.语音合成接口请求路径:/stream/v1/tts
         * 4.设置必须请求参数:appkey、token、text、format、sample_rate
         * 5.设置可选请求参数:voice、volume、speech_rate、pitch_rate
         */
        String url = "https://nls-gateway-cn-shenzhen.aliyuncs.com/stream/v1/tts";
        JSONObject taskObject = new JSONObject();
        taskObject.putOpt("appkey", nlsProperties.getAppKey());
        taskObject.putOpt("token", this.getToken());
        taskObject.putOpt("text", text);
        taskObject.putOpt("format", OutputFormatEnum.MP3.getName());
        taskObject.putOpt("voice", nlsProperties.getVoice());
        taskObject.putOpt("sample_rate", SampleRateEnum.SAMPLE_RATE_16K.value);
        // speech_rate 语速,范围是-500~500,可选,默认是0。
        taskObject.putOpt("speech_rate", nlsProperties.getSpeechRate());
        // pitch_rate 语调,范围是-500~500,可选,默认是0。
        taskObject.putOpt("pitch_rate", nlsProperties.getPitchRate());
        String bodyContent = JSONUtil.toJsonStr(taskObject);
        System.out.println("POST Body Content: " + bodyContent);
        RequestBody reqBody = RequestBody.create(MediaType.parse("application/json"), bodyContent);
        Request request = new Request.Builder()
                .url(url)
                .header("Content-Type", "application/json")
                .post(reqBody)
                .build();
        try {
            OkHttpClient client = new OkHttpClient();
            Response response = client.newCall(request).execute();
            String contentType = response.header("Content-Type");
            byte[] data = null;
            if ("audio/mpeg".equals(contentType)) {
                data = response.body().bytes();
                System.out.println("The POST request succeed!");
            } else {
                String errorMessage = response.body().string();
                System.out.println("The POST request failed: " + errorMessage);
            }
            response.close();
            if (data != null && data.length > 0) {

//                String filePath = "D:/test.wav";  //音频文件保存路径及文件名
//                saveFile(data, filePath);  //调用函数将音频保存到本地

                return data;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * token暂存redis中
     *
     *
     * */
    @SneakyThrows
    private String getToken() {
        String key = "NLS_TOKEN";
        Boolean hasKey = stringRedisTemplate.hasKey(key);
        if (BooleanUtil.isTrue(hasKey)) {
            return stringRedisTemplate.opsForValue().get(key);
        }
        AccessToken token = new AccessToken(nlsProperties.getAccessKeyId(), nlsProperties.getAccessKeySecret());
        token.apply();
        String accessToken = token.getToken();
        stringRedisTemplate.opsForValue().set(key, accessToken);
        stringRedisTemplate.expireAt(key, new Date(token.getExpireTime() * 1000));
        return accessToken;
    }

    /**
    * 文件转换与生成
    *
    * */
    public static void saveFile(byte[] bytes, String filePath) {
        try {
            FileOutputStream fos = new FileOutputStream(new File(filePath));
            fos.write(bytes);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


具体的配置可以参考官方文档 https://help.aliyun.com/product/30413.html?spm=a2c4g.750001.0.0.61184fadyUJTlQ ;
如果服务会频繁使用,可以把获取到阿里云token暂存在redis使用。

四、尾言

以上是语音合成业务的大致实现方案,感谢聆听和指正。后续笔者尝试或有改进的方案也会同步上去。

你可能感兴趣的:(后端的路,语音交互模块,IDEA,java,阿里云)