diff --git a/app/Http/Controllers/Admin/AudioController.php b/app/Http/Controllers/Admin/AudioController.php index 11b06291..7a69cd13 100644 --- a/app/Http/Controllers/Admin/AudioController.php +++ b/app/Http/Controllers/Admin/AudioController.php @@ -14,32 +14,21 @@ class AudioController extends Controller * * @return \Illuminate\Http\Response */ - public function index() + public function index(Request $request) { + if ($request->ajax()) { + $res = Audio::orderBy('id')->paginate($request->get('limit', 30)); + $data = [ + 'code' => 0, + 'msg' => '正在请求中...', + 'count' => $res->total(), + 'data' => $res->items(), + ]; + return response()->json($data); + } return view('admin.audio.index'); } - public function data(Request $request) - { - $res = Audio::orderBy('id')->paginate($request->get('limit', 30)); - $data = [ - 'code' => 0, - 'msg' => '正在请求中...', - 'count' => $res->total(), - 'data' => $res->items(), - ]; - return response()->json($data); - } - - /** - * Show the form for creating a new resource. - * - * @return \Illuminate\Http\Response - */ - public function create() - { - - } /** * Store a newly created resource in storage. @@ -53,84 +42,24 @@ class AudioController extends Controller if ($text==null){ return response()->json(['code'=>1,'msg'=>'请输入待合成文本']); } - $url = 'http://api.xfyun.cn/v1/service/v1/tts'; - $appid = config('freeswitch.xfyun.appid'); - $apikey = config('freeswitch.xfyun.apikey'); - $param = array ( - 'auf' => 'audio/L16;rate=8000', - 'aue' => 'raw', - 'voice_name' => 'xiaoyan', - 'speed' => '50', //这三个参数必需是字符串 - 'volume' => '50', //这三个参数必需是字符串 - 'pitch' => '50', //这三个参数必需是字符串 - 'engine_type' => 'intp65', - ); - $time = (string)time(); - $xparam = base64_encode(json_encode(($param))); - $checksum = md5($apikey.$time.$xparam); - $header = array( - 'X-CurTime:'.$time, - 'X-Param:'.$xparam, - 'X-Appid:'.$appid, - 'X-CheckSum:'.$checksum, - 'X-Real-Ip:127.0.0.1', - 'Content-Type:application/x-www-form-urlencoded; charset=utf-8' - ); - $content = [ - 'text' => $text, - ]; try{ - $response = $this->tocurl($url, $header, $content); - $header = $response['header']; - if($header['content_type'] == 'audio/mpeg'){ - $ext = $param['aue']=='raw'?'.wav':'.mp3'; - $filename = config('freeswitch.xfyun.sounds').$time.$ext; - file_put_contents($filename, $response['body']); - Audio::create(array_merge($param,[ - 'url' => $filename, - 'text' => $text + $model = new Audio(); + $res = $model->tts($text); + if ($res['code']==0) { + Audio::create([ + 'url' => $res['data']['url'], + 'path' => $res['data']['path'], + 'text' => $text, + 'user_id' => auth()->user()->id, ])); - return response()->json(['code'=>0,'msg'=>'合成成功','data'=>['url'=>$filename]]); + return response()->json(['code'=>0,'msg'=>'合成成功','data'=>['url'=>$res['data']['url']]]); } - return response()->json(['code'=>1,'msg'=>'合成失败','data'=>json_decode($response['body'],true)]); + return response()->json($res); }catch (\Exception $exception){ - return response()->json(['code'=>1,'msg'=>'合成失败:'.$exception->getMessage()]); + return response()->json(['code'=>1,'msg'=>'合成失败']); } } - /** - * Display the specified resource. - * - * @param int $id - * @return \Illuminate\Http\Response - */ - public function show($id) - { - // - } - - /** - * Show the form for editing the specified resource. - * - * @param int $id - * @return \Illuminate\Http\Response - */ - public function edit($id) - { - // - } - - /** - * Update the specified resource in storage. - * - * @param \Illuminate\Http\Request $request - * @param int $id - * @return \Illuminate\Http\Response - */ - public function update(Request $request, $id) - { - // - } /** * Remove the specified resource from storage. @@ -146,38 +75,15 @@ class AudioController extends Controller return response()->json(['code'=>1,'msg'=>'请选择删除项']); } try{ - if (file_exists($audio->url)){ - unlink($audio->url); + if (file_exists($audio->path)){ + unlink($audio->path); } $audio->delete(); return response()->json(['code'=>0,'msg'=>'删除成功']); }catch (\Exception $exception){ - return response()->json(['code'=>1,'msg'=>'删除失败:'.$exception->getMessage()]); + return response()->json(['code'=>1,'msg'=>'删除失败']); } } - public function tocurl($url, $header, $content){ - $ch = curl_init(); - if(substr($url,0,5)=='https'){ - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查 - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true); // 从证书中检查SSL加密算法是否存在 - } - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_HTTPHEADER, $header); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($content)); - $response = curl_exec($ch); - $error=curl_error($ch); - //var_dump($error); - if($error){ - die($error); - } - $header = curl_getinfo($ch); - - curl_close($ch); - $data = array('header' => $header,'body' => $response); - return $data; - } } diff --git a/app/Models/Audio.php b/app/Models/Audio.php new file mode 100644 index 00000000..020439d5 --- /dev/null +++ b/app/Models/Audio.php @@ -0,0 +1,109 @@ +get(config('freeswitch.baidu.url.token'),[ + 'query' => [ + 'grant_type' => 'client_credentials', + 'client_secret' => config('freeswitch.baidu.appKey'), + 'client_id' => config('freeswitch.baidu.appSecret'), + ] + ]); + $result = json_decode($response->getBody(),true); + $token = $result['access_token']; + Redis::setex($key,$result['expires_in']-100,$token); + }catch(\Exception $exception){ + $token = null; + } + } + return $token; + } + + public function tts($text){ + $token = $this->getAccessToken(); + if ($token==null) { + return ['code'=>1,'msg'=>'获取accessToken失败']; + } + $client = new Client(); + try{ + $response = $client->post(config('freeswitch.baidu.url.tts'),[ + 'body' => http_build_query([ + 'tex' => urlencode($text), + 'tok' => $token, + 'cuid' => 'freeswitch', + 'ctp' => 1, + 'lan' => 'zh', + 'spd' => 5 //语速0-15,默认为5中语速 + 'pit' => 5 //音调0-15,默认为5中音调 + 'vol' => 5 //音量0-15,默认为5中音量 + 'aue' => 6 //3为mp3格式(默认); 4为pcm-16k;5为pcm-8k;6为wav(内容同pcm-16k); 注意aue=4或者6是语音识别要求的格式 + 'per' => 4 //度小宇=1,度小美=0,度逍遥=3,度丫丫=4 ,度博文=106,度小童=110,度小萌=111,度米朵=103,度小娇=5 + ]) + ]); + if ($response->getHeader('Content-Type')=='audio/wav') { + $file_url = Str::random().'.wav'; + $file_path = public_path('uploads').'/'.$file_url; + Storage::disk('uploads')->put($file,$response->getBody()); + return ['code'=>0,'msg'=>'合成成功','data'=>['url'=>$file_url,'path'=>$file_path]; + } + return ['code'=>1,'msg'=>'合成失败']; + + }catch(\Exception $exception){ + return ['code'=>1,'msg'=>'合成异常']; + } + + } + + public function asr($file){ + $token = $this->getAccessToken(); + if ($token==null) { + return ['code'=>1,'msg'=>'获取accessToken失败']; + } + if (!file_exists($file)) { + return ['code'=>1,'msg'=>'文件不存在']; + } + $client = new Client(); + try{ + $response = $client->post(config('freeswitch.baidu.url.asr'),[ + 'body' => base64_encode(file_get_contents($file)), + 'json' => http_build_query([ + 'format' => 'wav', + 'rate' => 16000, + 'channel' => 1, + 'cuid' => 'freeswitch', + 'token' => $token, + 'dev_pid' => 1537 //语速0-15,默认为5中语速 + ]) + ]); + $result = json_decode($response->getBody(),true); + if (!$result['err_no']) { + return ['code'=>0,'msg'=>'识别成功','data'=>$result['result'][0]]; + } + return ['code'=>1,'msg'=>'识别失败']; + + }catch(\Exception $exception){ + return ['code'=>1,'msg'=>'识别异常']; + } + + } + +} diff --git a/config/freeswitch.php b/config/freeswitch.php index 9e01c9a6..48e84f35 100644 --- a/config/freeswitch.php +++ b/config/freeswitch.php @@ -81,4 +81,14 @@ return [ //自增ID的key,用于群呼时生成uuid 'callcenter_call' => 'callcenter_call_id', ], + 'baidu' => [ + 'appId' => '15143241', + 'appKey' => 'YI8bXhDcthkBrQNRQFDf2iCG', + 'appSecret' => 'NQo5Nk9rEZVAndjthwQd8OtYvNfjBcFM', + 'url' => [ + 'token' => 'https://openapi.baidu.com/oauth/2.0/token', + 'tts' => 'http://tsn.baidu.com/text2audio', + 'asr' => 'https://vop.baidu.com/pro_api', + ], + ], ]; \ No newline at end of file diff --git a/database/migrations/2020_05_14_103103_audio.php b/database/migrations/2020_05_14_103103_audio.php new file mode 100644 index 00000000..3eabd985 --- /dev/null +++ b/database/migrations/2020_05_14_103103_audio.php @@ -0,0 +1,36 @@ +bigIncrements('id'); + $table->string('text')->nullable()->comment('待合成文本'); + $table->string('url')->nullable()->comment('合成的语音文件的url'); + $table->string('path')->nullable()->comment('合成的语音文件的完整路径'); + $table->unsignedBigInteger('user_id')->comment('操作用户'); + $table->timestamps(); + }); + \DB::statement("ALTER TABLE `audio` comment '语音合成'"); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('audio'); + } +} diff --git a/database/seeds/MenuTableSeeder.php b/database/seeds/MenuTableSeeder.php index 2a028517..167179ce 100644 --- a/database/seeds/MenuTableSeeder.php +++ b/database/seeds/MenuTableSeeder.php @@ -218,6 +218,14 @@ class MenuTableSeeder extends Seeder 'type' => 1, 'permission_name' => 'data.cdr.count', ], + [ + 'name' => '语音合成', + 'route' => 'admin.audio', + 'url' => null, + 'icon' => 'layui-icon-ios', + 'type' => 1, + 'permission_name' => 'data.audio', + ], ] ], ]; diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php index fddeb2da..8c076f48 100644 --- a/database/seeds/UserTableSeeder.php +++ b/database/seeds/UserTableSeeder.php @@ -256,6 +256,14 @@ class UserTableSeeder extends Seeder ] ], + [ + 'name' => 'data.audio', + 'display_name' => '语音合成', + 'child' => [ + ['name' => 'data.audio.create', 'display_name' => '添加'], + ['name' => 'data.audio.destroy', 'display_name' => '删除'], + ] + ], ] ], ]; diff --git a/resources/views/admin/audio/index.blade.php b/resources/views/admin/audio/index.blade.php index f29b44ca..401fa314 100644 --- a/resources/views/admin/audio/index.blade.php +++ b/resources/views/admin/audio/index.blade.php @@ -16,7 +16,7 @@