php对接阿里云CosyVoice语音合成wss实现方式
这里请求使用webstocks方式实现,阿里云文档地址。2. 根据AccessKey和AccessKeySecret 生成token。1.获取AccessKey和AccessKeySecret。1.创建基类,用于实现公共部分。通过sdk获取token。1.获取鉴权需要的Appkey以及Token。2.新建应用类(语音合成实现类),继承上面父类。
1.获取鉴权需要的Appkey以及Token
- appkey 获取方式:
阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台
- token 获取方式:
1. 获取 AccessKey和AccessKeySecret

2. 根据AccessKey和AccessKeySecret 生成token
(1)安装Alibaba Cloud SDK for PHP SDK安装命令:
composer require alibabacloud/sdk
(2)php通过sdk获取token
/**
* @return string
*/
protected function getToken()
{
AlibabaCloud::accessKeyClient(
$accessKey,
$accessKeySecret)
->regionId("cn-shanghai")
->asDefaultClient();
try {
$response = AlibabaCloud::nlsCloudMeta()
->v20180518()
->createToken()
->request();
$token = $response["Token"];
if ($token != NULL) {
return $token['Id'];
}
else {
return false;
}
} catch (ClientException $exception) {
// 获取错误消息
$this->error = $exception->getMessage();
return false;
} catch (ServerException $exception) { // 获取错误消息
$this->error = $exception->getErrorMessage();
return false;
}
}
2.进行语音合成
1.创建基类,用于实现公共部分
<?php
namespace app\common\aliyun;
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use yii\caching\Cache;
abstract class Aliyun
{
/**
* 阿里云应用api_key
* @var string
*/
protected $api_key = '';
/**
* @var string
*/
protected $accessKeyId = '你的accessKey';
/**
* @var string
*/
protected $accessKeySecret = '你的accessKeySecret';
/**
* 控制台创建应用的key
* @var string
*/
protected $appKey = '你的appKey';
/**
* 自定义返回内容
* @var
*/
protected $return_json;
/**
* 错误信息
* @var string
*/
protected $error = '';
/**
* 返回错误
* @return string
*/
public function getError()
{
return $this->error;
}
/**
* @return string
*/
protected function getToken()
{
$ali_token = \Yii::$app->cache->get('aliyun_token');
if ($ali_token) {
return $ali_token;
}
AlibabaCloud::accessKeyClient(
$this->accessKeyId,
$this->accessKeySecret)
->regionId("cn-shanghai")
->asDefaultClient();
try {
$response = AlibabaCloud::nlsCloudMeta()
->v20180518()
->createToken()
->request();
$token = $response["Token"];
if ($token != NULL) {
\Yii::$app->cache->set('aliyun_token', $token['Id'],60);
return $token['Id'];
}
else {
return false;
}
} catch (ClientException $exception) {
// 获取错误消息
$this->error = $exception->getMessage();
return false;
} catch (ServerException $exception) { // 获取错误消息
$this->error = $exception->getErrorMessage();
return false;
}
}
/**
* 返回响应数据
* @return mixed
*/
public function getReturnJson() {
return $this->return_json;
}
}
2.新建应用类(语音合成实现类),继承上面父类
这里请求使用php webstocks方式实现,composer安装webstocks,命令:
(1) composer require cboden/ratchet
(2) composer require react/socket
这是我 composer.json 的内容

文档地址:生成式语音大模型服务_智能语音交互(ISI)-阿里云帮助中心
<?php
namespace app\common\aliyun;
use Ratchet\Client\Connector;
use React\EventLoop\Loop;
use React\Socket\Connector as SocketConnector;
class WsCosyVoice extends Aliyun
{
/**
* webstocket_url
* @var string
*/
protected $websocket_url = "wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1";
/**
* 输出文件路径
* @var string
*/
protected $output_file = '';
/**
* 音色名称
* @var mixed|string
*/
protected $voice_name = 'xiaoyun';
/**
* 合成文本
* @var false|string[]
*/
protected $texts;
public function __construct($texts, $output_file, $options = [])
{
$this->output_file = $output_file;
$this->texts = explode(',', $texts);
if (isset($options['voice_name'])) {
$this->voice_name = $options['voice_name'];
}
}
/**
* 启动
*/
public function run()
{
$loop = Loop::get();
if (file_exists($this->output_file)) {
// 清空文件内容
file_put_contents($this->output_file, '');
}
$socketConnector = new SocketConnector([
'tcp' => [
'bindto' => '0.0.0.0:0',
],
'tls' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
], $loop);
$connector = new Connector($loop, $socketConnector);
$output_file = $this->output_file;
$taskId = $this->generate32Id();
$token = $this->getToken();
if (!$token) {
return false;
}
$connector($this->websocket_url.'?token='.$token)->then(function ($conn) use ($loop, $output_file, $taskId) {
//连接到WebSocket服务器
// 发送 StartSynthesis 指令
$this->sendStartSynthesi($conn, $taskId);
// 定义发送上传识别文本指令的函数
$sendContinueTask = function () use ($conn, $loop, $taskId) {
// 待发送的文本
foreach ($this->texts as $text) {
$continueTaskMessage = json_encode([
"header" => [
'message_id'=> $this->generate32Id(),
'task_id'=> $taskId,
'namespace' => "FlowingSpeechSynthesizer",
"name"=> "RunSynthesis",
"appkey"=> $this->appKey
],
"payload" => [
"text" => $text,
]
]);
$conn->send($continueTaskMessage);
}
$this->sendFinishTaskMessage($conn, $taskId);
};
$taskStarted = false;
// 监听消息
$conn->on('message', function ($msg) use ($conn, $loop, $taskId, $output_file,$sendContinueTask,$taskStarted) {
if ($msg->isBinary()) {
// 写入二进制数据到本地文件
file_put_contents($output_file, $msg->getPayload(), FILE_APPEND);
} else {
// 处理非二进制消息
$response = json_decode($msg, true);
if (isset($response['header']['name'])) {
$this->handleEvent($conn, $response, $sendContinueTask, $loop, $taskId, $taskStarted);
}
}
});
// 监听连接关闭
$conn->on('close', function ($code = null, $reason = null) {
$this->return_json['code'] = 1;
});
}, function ($e) {
$this->return_json['code'] = 0;
$this->return_json['error_message'] = $e->getMessage();
});
$loop->run();
}
/**
* 生成32位ID
* @return string
*/
protected function generate32Id(): string
{
return bin2hex(random_bytes(16));
}
/**
* 开启StartSynthesis 指令
*/
public function sendStartSynthesi($conn, $taskId) {
$runTaskMessage = json_encode([
"header" => [
'message_id'=> $this->generate32Id(),
'task_id'=> $taskId,
'namespace' => "FlowingSpeechSynthesizer",
"name"=> "StartSynthesis",
"appkey"=> $this->appKey
],
"payload"=> [
"voice"=> $this->voice_name,
"format"=> "mp3",
"sample_rate"=> 16000,
"volume"=> 50,
"speech_rate"=> 0,
"pitch_rate"=> 0,
"enable_subtitle"=> true
]
]);
$conn->send($runTaskMessage);
}
/**
* 发送 StopSynthesis 指令
* @param $conn
* @param $taskId
*/
protected function sendFinishTaskMessage($conn, $taskId) {
$finishTaskMessage = json_encode([
"header" => [
'message_id'=> $this->generate32Id(),
'task_id'=> $taskId,
'namespace' => "FlowingSpeechSynthesizer",
"name"=> "StopSynthesis",
"appkey"=> $this->appKey
],
]);
// 准备发送StopSynthesis指令
$conn->send($finishTaskMessage);
}
/**
* 处理事件
* @param $conn
* @param $response
* @param $sendContinueTask
* @param $loop
* @param $taskId
* @param $taskStarted
*/
protected function handleEvent($conn, $response, $sendContinueTask, $loop, &$taskStarted) {
if ($response['header']['status'] == 20000000) {
switch ($response['header']['name']) {
case 'SynthesisStarted':
$taskStarted = true;
// 发送 RunSynthesis 指令
$sendContinueTask();
break;
case 'SynthesisCompleted': //所有音频以下发完毕
$taskStarted = false;
$conn->close();
break;
case 'TaskFailed':
$conn->close();
$this->return_json['code'] = $response['header']['status'];
$this->return_json['error_message'] = $response['header']['status_text'];
break;
}
}else {
// 错误信息
$this->return_json['code'] = $response['header']['status'];
$this->return_json['error'] = $response['header']['status_text'];
$conn->close();
}
// 如果任务已完成,关闭连接
if ($response['header']['name'] == 'SynthesisCompleted') {
// 等待1秒以确保所有数据都已传输完毕
$loop->addTimer(1, function() use ($conn) {
// 关闭客户端连接
$conn->close();
});
}
// 如果没有收到 SynthesisStarted 事件,关闭连接
if (!$taskStarted && in_array($response['header']['name'], ['SynthesisStarted'])) {
$conn->close();
}
}
}
3. 调用示例
// 输出合成语音的保存路径
$output_file = Yii::$app->basePath . 'web/uploads/demo.mp3';
// 合成的文本
$texts = '我是demo,测试哈哈哈';
// 初始化语音合成
$cosyVoice = new WsCosyVoice($output_file, $texts, [
'voice_name' => 'xiaoxun' // 合成语音的音色,可传入调用声音复刻返回video_name
]);
// 运行
$cosyVoice->run();
// 打印执行信息
$json = $cosyVoice->getReturnJson();
dump($json);exit();
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)