与推送系统集成
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Configuration;
using System.Threading;
using RepairerDispatch.SynthesisServer;
using System.Media;
using System.IO;
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
namespace RepairerDispatch
{
public partial class frmMain : Form
{
private Counter _Counter = new Counter();
private PushClient _PushClient = null;
protected SynchronizationContext SyncContext { get; set; }
public ConcurrentQueue<NotifyInfo> NotifyQueue = new ConcurrentQueue<NotifyInfo>();
public bool HandleEnable = false;
public int MaxSendCount = int.Parse(ConfigurationManager.AppSettings["RepeatCount"]);
public int RepeatInterval = int.Parse(ConfigurationManager.AppSettings["RepeatInterval"]);
public bool MockMode = bool.Parse(ConfigurationManager.AppSettings["Mock"]);
public bool Quietness = bool.Parse(ConfigurationManager.AppSettings["Quietness"]);
public static int FirstSendDelay = int.Parse(ConfigurationManager.AppSettings["Delay"]);
public frmMain()
{
InitializeComponent();
SyncContext = SynchronizationContext.Current;
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
#region 配置推送
_PushClient = new PushClient();
_PushClient.UserId = ConfigurationManager.AppSettings["UserId"];
_PushClient.OnPushReceived += this.HandleReceivePush;
_PushClient.Start();
#endregion
#region 启动消息处理队列
HandleEnable = true;
ThreadPool.QueueUserWorkItem(o => { HandleNotifyQueue(); }, null);
#endregion
#region 统计信息
var timer = new System.Windows.Forms.Timer();
timer.Interval = 100;
timer.Tick += (s, e) =>
{
try
{
lbl1.Text = string.Format("{0}/{1}/{2}", _Counter.Received, _Counter.SendCount, _Counter.CancelCount);
lbl2.Text = string.Format("{0}/{1}", _Counter.SynthesisCount, _Counter.SynthesisedCount);
lbl3.Text = _Counter.QueueLength.ToString();
}
catch { }
};
timer.Start();
#endregion
}
protected override void OnClosing(CancelEventArgs e)
{
if (MessageBox.Show("关闭窗体后将无法收到提示,确定关闭吗?", "警告!", MessageBoxButtons.YesNo,MessageBoxIcon.Warning) != System.Windows.Forms.DialogResult.Yes)
{
e.Cancel = true;
}
base.OnClosing(e);
}
protected override void OnClosed(EventArgs e)
{
try
{
HandleEnable = false;
if (_PushClient != null)
{
_PushClient.OnPushReceived -= this.HandleReceivePush;
_PushClient.Dispose();
}
}
finally { }
base.OnClosed(e);
}
#region 处理推送消息
private void HandleReceivePush(string msg)
{
if (this.InvokeRequired)
{
SyncContext.Post((ox) => { HandleReceivePush(msg); }, null);
}
else
{
try
{
Interlocked.Increment(ref _Counter.Received);
WriteInfo(msg.Replace(Environment.NewLine,""));
var info= NotifyInfo.Create(msg);
if (info == null) return;
if (info.VoiceReady)
{
NotifyQueue.Enqueue(info);
_Counter.QueueLength ++;
}
else
{
SynthesisVoice(info);
}
}
catch (Exception ex)
{
WriteInfo(ex.Message);
}
}
}
/// <summary>
/// 处理队列里的消息
/// </summary>
private void HandleNotifyQueue()
{
try
{
while (HandleEnable)
{
//WriteInfo("执行通知队列处理" + DateTime.Now);
var info=new NotifyInfo();
_Counter.QueueLength = NotifyQueue.Count;
if (NotifyQueue.TryDequeue(out info))
{
//符合播放条件
if (info.SendTime <= DateTime.Now )
{
#region 播放
using (var ctx = new JL_MFGEntities())
{
var it = ctx.L_Conn_CallRepair.FirstOrDefault(ent => ent.ID == info.MsgId);
if (it == null || it.TicketStatus != "呼叫" || !string.IsNullOrWhiteSpace(it.TakeOverEmpId))
{
if (!MockMode)//模拟模式
{
Interlocked.Increment(ref _Counter.CancelCount);
continue;
}
}
}
Interlocked.Increment(ref _Counter.SendCount);
if (!Quietness)
{
using (SoundPlayer soundPlayer = new SoundPlayer(info.WavFile))
{
soundPlayer.Stop();
soundPlayer.PlaySync();
Thread.Sleep(1000 * 6);
}
}
info.SendCount++;
info.SendTime = info.SendTime.AddMinutes(RepeatInterval);
if (info.SendCount < MaxSendCount)
{
NotifyQueue.Enqueue(info);
}
#endregion
}
else
{
NotifyQueue.Enqueue(info);
}
}
_Counter.QueueLength = NotifyQueue.Count;
if (NotifyQueue.Count <= 0)
{
Thread.Sleep(450);
}
Thread.Sleep(50);
}
}
catch (Exception ex)
{
WriteInfo("错误" + ex.Message);
}
finally
{
if (HandleEnable)
{
ThreadPool.QueueUserWorkItem(o => { HandleNotifyQueue(); }, null);
}
}
}
private void SynthesisVoice(NotifyInfo info)
{
try
{
#region 合成音频并排队
using (var svr = new SynthesisService())
{
WriteInfo(DateTime.Now + ":发起音频合成..." );
Interlocked.Increment(ref _Counter.SynthesisCount);
svr.Timeout = 1000 * 360;
svr.GetVoiceCompleted += (s, e) =>
{
try
{
if (_Counter.SynthesisCount > 0)Interlocked.Decrement(ref _Counter.SynthesisCount);
if (e.Error != null) throw e.Error;
if (e.Cancelled) return;
if (e.Result.Code != 0) throw new Exception(e.Result.Msg);
var filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "VCache/" + info.Postion + ".wav");
if(!Directory.Exists(Path.GetDirectoryName( filename)))
{
Directory.CreateDirectory(Path.GetDirectoryName(filename));
}
File.WriteAllBytes(filename, e.Result.Data);
info.VoiceReady = true;
NotifyQueue.Enqueue(info);
Interlocked.Increment(ref _Counter.SynthesisedCount);
_Counter.QueueLength ++;
WriteInfo(DateTime.Now +":完成音频合成");
}
catch (Exception ex)
{
WriteInfo(ex.Message);
}
};
svr.GetVoiceAsync(info.Msg, info);
}
#endregion
}
catch (Exception ex)
{
WriteInfo(ex.Message);
}
}
#endregion
private void btnTest_Click(object sender, EventArgs e)
{
try
{
btnTest.Enabled = false;
TestOnlineSynthesis();
#region 保持3秒
for (int i = 0; i < 30; i++)
{
Thread.Sleep(100);
Application.DoEvents();
}
#endregion
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
btnTest.Enabled = true;
}
}
private void TestOnlineSynthesis()
{
Action act = () =>
{
try
{
#region 在线合成测试
Stopwatch sw = new Stopwatch();
WriteInfo(DateTime.Now +":发起在线合成测试...");
Interlocked.Increment(ref _Counter.SynthesisCount);
sw.Start();
var svr = new SynthesisService();
svr.Timeout = 1000 * 360;
var msg = ConfigurationManager.AppSettings["TestText"];
var response = svr.GetVoice(msg);
//更新计数器
if (_Counter.SynthesisCount > 0) Interlocked.Decrement(ref _Counter.SynthesisCount);
if (response.Code != 0) throw new Exception(response.Msg);
Interlocked.Increment(ref _Counter.SynthesisedCount);
using (MemoryStream ms = new MemoryStream(response.Data))
{
using (SoundPlayer soundPlayer = new SoundPlayer(ms))
{
soundPlayer.Stop();
soundPlayer.Play();
}
}
sw.Stop();
WriteInfo(string.Format("{0}:完成在线合成测试,用时{1}毫秒",DateTime.Now,sw.ElapsedMilliseconds));
#endregion
}
catch (Exception ex)
{
WriteInfo(ex.Message);
}
};
var ar = act.BeginInvoke(null, null);
}
private void WriteInfo(string msg)
{
if (InvokeRequired)
{
SyncContext.Post(o =>
{
WriteInfo(msg);
}, null);
}
else
{
lblTips.Text = msg;
Console.WriteLine(msg);
}
}
public class NotifyInfo
{
public long MsgId { get; set; }
public String Postion { get; set; }
public String Msg { get; set; }
public String WavFile { get; set; }
public DateTime AddTime { get; set; }
public DateTime SendTime { get; set; }
public int SendCount { get; set; }
public bool VoiceReady { get; set; }
public String EmpName { get; set; }
public int EmpId { get; set; }
public String Memo { get; set; }
public String MachineType { get; set; }
#region 机台编号对应表
public static Dictionary<String, String> MachineTypeDic =
new Dictionary<string, string>()
{
{"ZD","自动机"},
{"YJ","压接"},
{"ZJ","中间压接"},
{"DJ","冲床"},
{"XL","电脑剥线机"},
{"QD","扩孔机"},
{"JC","绞缠线"},
{"BJ","包胶机"},
{"RS","热缩机"},
{"YT","一体机"},
{"SC","超声波"}
};
#endregion
public NotifyInfo()
{
AddTime = DateTime.Now;
SendCount = 0;
WavFile = "";
VoiceReady = false;
SendTime = DateTime.Now;
}
public static NotifyInfo Create(String msg)
{
var info = new NotifyInfo();
var MsgTemp = ConfigurationManager.AppSettings["MsgTemp"];
if (msg.IndexOf("呼叫机修通知") < 0) return null;
//呼叫机修通知:($RecId$)\r\n机台:$Position$\r\n员工:$EmpName$($EmpId$)\r\n备注:$Memo$
//呼叫机修通知:(5168) 机台:ZD062 员工:杜多婷(170062) 备注:
var m = Regex.Match(msg, @"^呼叫机修通知:(.*?)机台:(.*?)员工:(.*?)备注:(.*?)$", RegexOptions.Singleline| RegexOptions.IgnoreCase);
if(!m.Success)return null;
info.AddTime = DateTime.Now;
info.SendTime = DateTime.Now.AddMinutes( frmMain.FirstSendDelay);
info.MsgId =long.Parse( m.Groups[1].Value.Replace("(", "").Replace(")",""));
info.Postion = m.Groups[2].Value.Trim().ToUpper();
//员工与工号
var empStr=m.Groups[3].Value;
var index=empStr.IndexOf("(");
info.EmpName = empStr.Substring(0, index);
var endIndex=empStr.IndexOf(")");
info.EmpId= int.Parse(empStr.Substring(index+1, endIndex-index-1));
info.Memo = m.Groups[4].Value.Trim();
var m2 = Regex.Match(info.Postion, @"([a-zA-Z]*)\d*", RegexOptions.Singleline | RegexOptions.IgnoreCase);
if (!m2.Success) return null;
info.MachineType = m2.Groups[1].Value.Trim();
var mNum = info.Postion.Replace(info.MachineType, "").TrimStart("0".ToCharArray()).Trim();
info.Msg =MsgTemp.Replace("$工序$", MachineTypeDic[info.MachineType]).Replace("$编号$",info.Postion).Replace("$数字$",mNum);
var filename= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "VCache/" + info.Postion + ".wav");
info.WavFile = filename;
info.VoiceReady=File.Exists(filename);
return info;
}
}
public class Counter
{
public int SynthesisCount = 0; //正在合成数
public int SynthesisedCount = 0; //已合成数
public int Received = 0;
public int SendCount = 0;
public int CancelCount = 0;
public int QueueLength = 0;
}
}
}
View Code
服务端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using TTS;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Configuration;
namespace VoiceSynthesis
{
/// <summary>
/// SynthesisService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。
// [System.Web.Script.Services.ScriptService]
public class SynthesisService : System.Web.Services.WebService
{
int ret = 0;
IntPtr session_ID;
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
#region Core
/// <summary>
/// int>0 声音文件编号
/// 科户端更具这个获取具体名称
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
[WebMethod]
public ConvertResult GetVoice(string msg)
{
var response = new ConvertResult() { Code = 0, Msg = "", VoiceId = "" };
try
{
var vid=Path.GetRandomFileName()+".v";
var filename=Server.MapPath("/tmp/"+vid);
List<byte> outData;
lock (typeof(SynthesisService))
{
GenWave(filename, msg,out outData);
}
response.Data = outData;
response.VoiceId = vid;
}
catch (Exception ex)
{
response.Code = -1;
response.Msg = ex.Message;
}
return response;
}
private void GenWave(string fname, string msg,out List<byte> fileBytes)
{
try
{
fileBytes = null;
///APPID请勿随意改动
string login_configs = "appid = 57f2ed75xx";//登录参数,自己注册后获取的appid
var text = msg;
string filename = fname; //合成的语音文件
uint audio_len = 0;
SynthStatus synth_status = SynthStatus.MSP_TTS_FLAG_STILL_HAVE_DATA;
ret = TTSDll.MSPLogin(string.Empty, string.Empty, login_configs);//第一个参数为用户名,第二个参数为密码,第三个参数是登录参数,用户名和密码需要在http://open.voicecloud.cn
//MSPLogin方法返回失败
if (ret != (int)ErrorCode.MSP_SUCCESS)
{
return;
}
//string parameter = "engine_type = local, voice_name=xiaoyan, tts_res_path =fo|res\\tts\\xiaoyan.jet;fo|res\\tts\\common.jet, sample_rate = 16000";
//string _params = "ssm=1,ent=sms16k,vcn=xiaoyan,spd=medium,aue=speex-wb;7,vol=x-loud,auf=audio/L16;rate=16000";
//许工,你妈妈喊你回家吃饭
string _params =ConfigurationManager.AppSettings["VSet"] ;
//string @params = "engine_type = local,voice_name=xiaoyan,speed=50,volume=50,pitch=50,rcn=1, text_encoding = UTF8, background_sound=1,sample_rate = 16000";
session_ID = TTSDll.QTTSSessionBegin(_params, ref ret);
//QTTSSessionBegin方法返回失败
if (ret != (int)ErrorCode.MSP_SUCCESS)
{
return;
}
ret = TTSDll.QTTSTextPut(Ptr2Str(session_ID), text, (uint)Encoding.Default.GetByteCount(text), string.Empty);
//QTTSTextPut方法返回失败
if (ret != (int)ErrorCode.MSP_SUCCESS)
{
return;
}
MemoryStream memoryStream = new MemoryStream();
memoryStream.Write(new byte[44], 0, 44);
while (true)
{
IntPtr source = TTSDll.QTTSAudioGet(Ptr2Str(session_ID), ref audio_len, ref synth_status, ref ret);
byte[] array = new byte[(int)audio_len];
if (audio_len > 0)
{
Marshal.Copy(source, array, 0, (int)audio_len);
}
memoryStream.Write(array, 0, array.Length);
Thread.Sleep(1000);
if (synth_status == SynthStatus.MSP_TTS_FLAG_DATA_END || ret != 0)
break;
}
WAVE_Header wave_Header = getWave_Header((int)memoryStream.Length - 44);
byte[] array2 = this.StructToBytes(wave_Header);
memoryStream.Position = 0L;
memoryStream.Write(array2, 0, array2.Length);
memoryStream.Position = 0L;
//SoundPlayer soundPlayer = new SoundPlayer(memoryStream);
//soundPlayer.Stop();
//soundPlayer.Play();
fileBytes = new List<byte>(memoryStream.ToArray());
if (filename != null)
{
FileStream fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write);
memoryStream.WriteTo(fileStream);
memoryStream.Close();
fileStream.Close();
}
}
catch (Exception )
{
throw;
}
finally
{
ret = TTSDll.QTTSSessionEnd(Ptr2Str(session_ID), "");
ret = TTSDll.MSPLogout();//退出登录
}
}
/// <summary>
/// 结构体转字符串
/// </summary>
/// <param name="structure"></param>
/// <returns></returns>
private byte[] StructToBytes(object structure)
{
int num = Marshal.SizeOf(structure);
IntPtr intPtr = Marshal.AllocHGlobal(num);
byte[] result;
try
{
Marshal.StructureToPtr(structure, intPtr, false);
byte[] array = new byte[num];
Marshal.Copy(intPtr, array, 0, num);
result = array;
}
finally
{
Marshal.FreeHGlobal(intPtr);
}
return result;
}
/// <summary>
/// 结构体初始化赋值
/// </summary>
/// <param name="data_len"></param>
/// <returns></returns>
private WAVE_Header getWave_Header(int data_len)
{
return new WAVE_Header
{
RIFF_ID = 1179011410,
File_Size = data_len + 36,
RIFF_Type = 1163280727,
FMT_ID = 544501094,
FMT_Size = 16,
FMT_Tag = 1,
FMT_Channel = 1,
FMT_SamplesPerSec = 16000,
AvgBytesPerSec = 32000,
BlockAlign = 2,
BitsPerSample = 16,
DATA_ID = 1635017060,
DATA_Size = data_len
};
}
/// <summary>
/// 语音音频头
/// </summary>
private struct WAVE_Header
{
public int RIFF_ID;
public int File_Size;
public int RIFF_Type;
public int FMT_ID;
public int FMT_Size;
public short FMT_Tag;
public ushort FMT_Channel;
public int FMT_SamplesPerSec;
public int AvgBytesPerSec;
public ushort BlockAlign;
public ushort BitsPerSample;
public int DATA_ID;
public int DATA_Size;
}
/// 指针转字符串
/// </summary>
/// <param name="p">指向非托管代码字符串的指针</param>
/// <returns>返回指针指向的字符串</returns>
public static string Ptr2Str(IntPtr p)
{
List<byte> lb = new List<byte>();
while (Marshal.ReadByte(p) != 0)
{
lb.Add(Marshal.ReadByte(p));
p = p + 1;
}
byte[] bs = lb.ToArray();
return Encoding.Default.GetString(lb.ToArray());
}
public class ConvertResult
{
public int Code { get; set; }
public String VoiceId { get; set; }
public String Msg { get; set; }
public List<Byte> Data { get; set; }
}
#endregion
}
}
View Code
IIS 7.0配置成32位模式,运行在LocalSystem下
<appSettings>
<add key="UserId" value="8999001" />
<add key="MsgTemp" value="简讯!{0}:{1}呼叫机修,{0},{1}呼叫机修,完毕!"/>
<add key="RepeatCount" value="3"/> <!-- 重复播放次数 -->
<add key="RepeatInterval" value="3"/><!-- 重复间隔时间,单位分钟 -->
<add key="TestText" value="喜讯!水工,你媳妇又生了,你妈叫你快些子回家,看看生了个啥子!"/>
<add key="Mock" value="true"/>
</appSettings>
View Code