package com.yeejoin.amos.speech;

import com.alibaba.nls.client.protocol.InputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriber;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;


/**
 * 实时语音识别
 * <p>
 * 支持的输入格式：PCM（无压缩的PCM或WAV文件）、16 bit采样位数、单声道（mono）。
 * 支持的音频采样率：8000 Hz和16000 Hz。
 */
public class AppSpeechTranscriber {

    private static final Logger logger = LoggerFactory.getLogger(AppSpeechTranscriber.class);
    private final SpeechTranscriberListener listener;
    private final ServerSocket serverSocket;
    private final Timer serverSocketTimeoutTimer;
    private final TimerTask timerTask;

    public AppSpeechTranscriber(SpeechTranscriberListener listener, ServerSocket serverSocket) {
        this.listener = listener;
        this.serverSocket = serverSocket;
        serverSocketTimeoutTimer = new Timer();
        //60s未收到任何请求，直接关闭监听
        timerTask = new TimerTask() {
            @Override
            public void run() {
                logger.warn("serverSocket,port:" + serverSocket.getLocalPort() + " 等待60s无应答即将自动关闭！");
                closeServerSocket();
            }
        };
        serverSocketTimeoutTimer.schedule(timerTask, 1000 * 60);
    }

    /**
     * 开始语音识别
     */
    public void process() {
        SpeechTranscriber transcriber = null;
        try {
            //启动ServerSocket等待接收音频数据,只接受一次请求
            logger.warn("serverSocket已启动，地址：" + InetAddress.getLocalHost().getHostAddress()
                    + "监听端口：" + serverSocket.getLocalPort() + "  等待语音融合系统推送数据...");
            Socket socket = serverSocket.accept();
            timerTask.cancel();
            serverSocketTimeoutTimer.cancel();
            logger.warn("收到用户连接请求，开始读取数据");
            //创建实例、建立连接。
            transcriber = new SpeechTranscriber(AppNslClient.instance(), listener);
            //设置识别参数
            setParam(transcriber);

            transcriber.start();
            InputStream inputStream = socket.getInputStream();
            byte[] b = new byte[4096];
            int len;
            while ((len = inputStream.read(b)) > 0) {
                logger.info("receive data pack length: " + len);
                transcriber.send(b, len);
            }
            socket.close();
            transcriber.stop();
            logger.warn("语音转文字已结束");
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally {
            if (null != transcriber) {
                transcriber.close();
            }
            closeServerSocket();
        }
    }

    /**
     * 关闭Serversocket
     */
    public void closeServerSocket() {
        if (serverSocket != null && !serverSocket.isClosed()) {
            try {
                serverSocket.close();
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
    }

    /**
     * 设置识别参数
     */
    private void setParam(SpeechTranscriber transcriber) {
        transcriber.setAppKey(SpeechConfig.AppKey);
        //输入音频编码方式。
        transcriber.setFormat(InputFormatEnum.PCM);
        //输入音频采样率。
        transcriber.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
        //是否返回中间识别结果。
        transcriber.setEnableIntermediateResult(true);
        //是否生成并返回标点符号。
        transcriber.setEnablePunctuation(true);
        //是否将返回结果规整化，比如将一百返回为100。
        transcriber.setEnableITN(true);
        //设置vad断句参数。默认值：800ms，有效值：200ms～2000ms。
        transcriber.addCustomedParam("max_sentence_silence", 500);
        //设置是否语义断句。
        //transcriber.addCustomedParam("enable_semantic_sentence_detection",false);
        //设置是否开启顺滑。
        //transcriber.addCustomedParam("disfluency",true);
        //设置是否开启词模式。
        transcriber.addCustomedParam("enable_words", true);
        //设置vad噪音阈值参数，参数取值为-1～+1，如-0.9、-0.8、0.2、0.9。
        //取值越趋于-1，判定为语音的概率越大，亦即有可能更多噪声被当成语音被误识别。
        //取值越趋于+1，判定为噪音的越多，亦即有可能更多语音段被当成噪音被拒绝识别。
        //该参数属高级参数，调整需慎重和重点测试。
        //transcriber.addCustomedParam("speech_noise_threshold",0.3);
        //设置训练后的定制语言模型id。
        //transcriber.addCustomedParam("customization_id","你的定制语言模型id");
        //设置训练后的定制热词id。
        //transcriber.addCustomedParam("vocabulary_id","你的定制热词id");
        //设置是否忽略单句超时。
        transcriber.addCustomedParam("enable_ignore_sentence_timeout", false);
        //vad断句开启后处理。
        //transcriber.addCustomedParam("enable_vad_unify_post",false);
        //此方法将以上参数设置序列化为JSON发送给服务端，并等待服务端确认。
    }
}
