<?php
// +----------------------------------------------------------------------
// | likeadmin快速开发前后端分离管理后台(PHP版)
// +----------------------------------------------------------------------
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
// | 开源版本可自由商用,可去除界面版权logo
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
// | github下载:https://github.com/likeshop-github/likeadmin
// | 访问官网:https://www.likeadmin.cn
// | likeadmin团队 版权所有 拥有最终解释权
// +----------------------------------------------------------------------
// | author: likeadminTeam
// +----------------------------------------------------------------------

namespace app\api\controller;

use IFlytek\Xfyun\Speech\ChatClient;
use IFlytek\Xfyun\Speech\IatClient;
use IFlytek\Xfyun\Speech\TtsClient;
use WebSocket\Client;

/**
 * 讯飞
 * Class WechatController
 */
class XunFeiController extends BaseApiController
{
    public array $notNeedLogin = ['chat', 'iat', 'tts'];

    private $app_id='2eda6c2e';

    private $api_key='12ec1f9d113932575fc4b114a2f60ffd';

    private $api_secret='MDEyMzE5YTc5YmQ5NjMwOTU1MWY4N2Y2';

    public function chat()
    {
        header('X-Accel-Buffering: no');
        $parmas=$this->request->param('content');
        if(empty($parmas)){
            return $this->success('success');
        }
        $chat=new ChatClient($this->app_id,$this->api_key,$this->api_secret);
        $client = new Client($chat->assembleAuthUrl('wss://spark-api.xf-yun.com/v2.1/chat'));
        // 连接到 WebSocket 服务器
        if ($client) {
            // 发送数据到 WebSocket 服务器
            $data = $this->getBody($this->app_id,$parmas);

            $client->send($data);
            // 从 WebSocket 服务器接收数据
            $answer = "";
            while(true){
                $response = $client->receive();
                $resp = json_decode($response,true);
                $code = $resp["header"]["code"];
                // echo "从服务器接收到的数据: " . $response;
                if(0 == $code){
                    $status = $resp["header"]["status"];
                    if($status != 2){
                        $content = $resp['payload']['choices']['text'][0]['content'];
                        $answer .= $content;
                        print($answer);
                        ob_flush(); // 刷新输出缓冲区
                        flush(); // 刷新系统输出缓冲区
                    }else{
                        $content = $resp['payload']['choices']['text'][0]['content'];
                        $answer .= $content;
                        $total_tokens = $resp['payload']['usage']['text']['total_tokens'];
                        print("\n本次消耗token用量:\n");
                        print($total_tokens);
                        ob_flush(); // 刷新输出缓冲区
                        flush(); // 刷新系统输出缓冲区
                        break;
                    }
                }else{
                    return $this->fail( "服务返回报错".$response);
                    break;
                }
            }
            ob_flush(); // 刷新输出缓冲区
            flush(); // 刷新系统输出缓冲区
            return $this->success('success');

        } else {
            return $this->fail('无法连接到 WebSocket 服务器');
        }
    }

    //构造参数体
    function getBody($appid,$question){
        $header = array(
            "app_id" => $appid,
            "uid" => "1"
        );
        
        $parameter = array(
            "chat" => array(
                "domain" => "generalv2",
                "temperature" => 0.5,
                "max_tokens" => 1024
            )
        );
        
        $payload = array(
            "message" => array(
                "text" => array(
                    array("role" => "user", "content" => $question)
                )
            )
        );
        
        $json_string = json_encode(array(
            "header" => $header,
            "parameter" => $parameter,
            "payload" => $payload
        ));

        return $json_string;

    }

    //语音听写(流式版)
    public function iat()
    {
        header('X-Accel-Buffering: no');
        $file = request()->file('audio');
        /*
        if(empty($file)){
            //return $this->fail('未上传音频文件');
        }
        // 上传音频临时文件
        $savename = \think\facade\Filesystem::putFile('audio', $file);
        $file = app()->getRootPath() . '/runtime/storage/' . $savename;
        if (!file_exists($file)) {
            //return $this->fail('未上传音频文件');
        }
        */
        $file = "https://lihai001.oss-cn-chengdu.aliyuncs.com/media/iat_mp3_16k.mp3";
        $audioFile = fopen($file, 'rb');
        if ($audioFile === false) {
            return $this->fail('音频文件异常');
        }
        $words = '';
        $iatHostUrl = "wss://iat-api.xfyun.cn/v2/iat";
        $iat = new IatClient($this->app_id,$this->api_key,$this->api_secret);
        $client = new Client($iat->assembleAuthUrl($iatHostUrl));
        if ($client) {
            $frameSize = 1280; //每一帧的音频大小
            $intervel = 20 * 1000; //发送音频间隔
            $status = 0;
            while (true) {
                $len = fread($audioFile, $frameSize);
                if ($len === false) {
                    break;
                }
                if ($len === '') { //文件读取完了
                    $status = 2;
                }
                switch ($status) {
                    case 0: //发送第一帧音频,带business 参数
                        $frameData = array(
                            'common' => array(
                                'app_id' => $this->app_id //appid 必须带上,只需第一帧发送
                            ),
                            'business' => array( //business 参数,只需一帧发送
                                'language' => 'zh_cn',
                                'domain' => 'iat',
                                'accent' => 'mandarin'
                            ),
                            'data' => array(
                                'status' => 0,
                                'format' => 'audio/L16;rate=16000',
                                'audio' => base64_encode($len),
                                'encoding' => 'lame'
                            )
                        );
                        $client->send(json_encode($frameData));
                        $status = 1;
                        break;
                    case 1:
                        $frameData = array(
                            'data' => array(
                                'status' => 1,
                                'format' => 'audio/L16;rate=16000',
                                'audio' => base64_encode($len),
                                'encoding' => 'raw'
                            )
                        );
                        $client->send(json_encode($frameData));
                        break;
                    case 2:
                        $frameData = array(
                            'data' => array(
                                'status' => 2,
                                'format' => 'audio/L16;rate=16000',
                                'audio' => base64_encode($len),
                                'encoding' => 'raw'
                            )
                        );
                        $client->send(json_encode($frameData));
                        break 2;
                }
                //模拟音频采样间隔
                usleep($intervel);
            }
            while (true) {
                $response = $client->receive();
                if ($response === null) {
                    break;
                }
                $resp = json_decode($response,true);
                $code = $resp['code'];
                $message = $resp['message'];
                $data = $resp['data'];
                $result = $data['result'];
                $status = $data['status'];
                foreach($result['ws'] as $v) {
                    $words .= $v['cw'][0]['w'] ?? '';
                }
                if ($code != 0) {
                    break;
                }
                if ($status === 2) {
                    break;
                }
            }
        } else {
            return $this->fail('无法连接到 WebSocket 服务器');
        }
        fclose($audioFile);
        // 删除临时音频文件
        if (file_exists($file)) {
            unlink($file);
        }
        return $this->data(['words' => $words]);
    }

    //语音合成(流式版)
    public function tts() 
    {
        halt(request()->domain() . request()->host());
        header('X-Accel-Buffering: no');
        $ttsHostUrl = "wss://tts-api.xfyun.cn/v2/tts";
        $text = request()->param('text');
        if(empty($text)){
            return $this->fail('未上传文本参数');
        }
        $date = date('YmdHis', time());
        $file_name = $date . mt_rand(1000, 9999) . '.mp3';
        $dir = app()->getRootPath() . '/public/uploads/audio/' . date('Ymd');
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
        $audioFile = $dir . '/' . $file_name;
        $business = [
            'aue' => 'lame', //mp3格式
            'vcn' => 'xiaoyan', //发音人
            'auf' => 'audio/L16;rate=16000', //音频采样率
            'speed' => 50, //语速
            'volume'=> 100, //音量
            'pitch' => 50, //音高
            'tte' => 'UTF8'
        ];
        try {
            $tts = new TtsClient($this->app_id, $this->api_key, $this->api_secret, $business);
            file_put_contents($audioFile, $tts->request($text)->getBody()->getContents());
        } catch (\Exception $e) {
            return $this->fail($e->getMessage().'');
        }
        return $this->data(['audio_file' => request()->domain() . '/uploads/audio/' . date('Ymd') . '/' . $file_name]);
    }

}