遗忘海岸

江湖程序员 -Feiph(LM战士)

导航

自动语音播报系统

与推送系统集成

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

配置

posted on 2016-10-21 14:05  遗忘海岸  阅读(1246)  评论(0编辑  收藏  举报