如今不少企業都採用freeswitch搭建的軟交換來實現通話,主要優點成本低吞吐量大,可是語音卡的通話質量仍是瑞勝一籌。redis
去年有機會在朋友公司裏幫忙開發與軟交換交互的上層服務及接口,在開發過程當中稍微研究了下飛環的語音卡,並用c#實現了簡單的單方通話及雙向通話,下面就把單方通話的實現方法簡單說下c#
//打開設備驅動 [DllImport("PhonicDT.dll")] public extern static int tpi_OpenDevice();
/// <summary> /// 獲取指定類型的通道的數量 /// </summary> /// <param name="channelType">通道類型,能夠是EChannelType所定義的任一種,參看類型定義</param> /// <returns>大於0 爲通道數,0表示沒有該類型通道,小於0 表示出錯的錯誤碼,參看EDtvcErrorCode 定義</returns> [DllImport(DllName.PhonicName)] public extern static int tpi_GetChannelCount(int channelType);
/// <summary> /// 通道實體 /// </summary> public class ChannelModel { #region proptery /// <summary> /// 通道Id(通道號) /// </summary> public int ChannelId { get; set; } /// <summary> /// 所屬組Id /// </summary> public int GroupId { get; set; } /// <summary> /// 通道類型 /// </summary> public EChannelType ChannelType { get; set; } /// <summary> /// 通道狀態 /// </summary> public EChannelState ChannelState { get; set; } /// <summary> /// 通道屬性 /// </summary> public EVoiceChannelAttrib VoiceChannelAttrib { get; set; } /// <summary> /// 通道所屬的流號 /// </summary> public long StreamNo { get; set; } /// <summary> /// 通道所屬的時序 /// </summary> public long TimeSlot { get; set; } /// <summary> /// 是否正在放音 /// </summary> public bool IsPlay { get; set; } /// <summary> /// 通道任務 /// </summary> public TaskModel Task { get; set; } /// <summary> /// 通道觸發的事件 /// </summary> public EPhonicEvent EventState { get; set; } /// <summary> /// 錄音文件地址 /// </summary> public string VoiceFilePath { get; set; } #endregion #region func /// <summary> /// 呼叫 /// </summary> /// <param name="isTo">true呼叫客戶,false呼叫坐席</param> public void MakeCall(bool isTo) { string callStr = this.Task.ToCall; if (!isTo) { callStr = this.Task.Agent; } CallImport.tpi_MakeCall( Convert.ToInt32(this.ChannelType) , this.ChannelId , new StringBuilder(this.Task.FromCall) , new StringBuilder(callStr) , this.Task.CallRingingTime); //修改通道狀態 this.ChannelState = EChannelState.STATE_OUT_CALLING; } /// <summary> /// 根據文件放音 /// </summary> public void Play() { //"E:\\test\\financial.vox" StringBuilder filename = new StringBuilder(this.VoiceFilePath);//物理路徑 VoiceService.PlayFile(Convert.ToInt32(this.ChannelType), this.ChannelId, filename, 0, this.Task.PlayTime); this.IsPlay = true; } /// <summary> /// 從內存放音 /// </summary> public void PlayMemory() { //將語音流存入內存中 "E:\\test\\financial.vox" using (FileStream fs = new FileStream("E:\\test\\financial.vox", FileMode.Open)) { byte[] array = new byte[fs.Length];//初始化字節數組 fs.Read(array, 0, array.Length);//讀取流中數據把它寫到字節數組中 ASCIIEncoding encoding = new ASCIIEncoding(); string pVoiceBuffer = System.Text.Encoding.Default.GetString(array); //encoding.GetString(array); //Console.WriteLine(pVoiceBuffer); // VoiceService.PlayMemory(Convert.ToInt32(this.ChannelType), this.ChannelId, ref pVoiceBuffer, 0); this.IsPlay = true; } } /// <summary> /// 中止放音 /// </summary> public void StopPlay() { VoiceImport.tpi_StopPlay(Convert.ToInt32(this.ChannelType), this.ChannelId); this.IsPlay = false; } /// <summary> /// 釋放通道 /// </summary> public void Destroy() { this.ChannelState = EChannelState.STATE_IDLE; this.Task = null; this.EventState = EPhonicEvent.eventIdle; } #endregion }
public class ChannelService { private static ChannelService channelService = new ChannelService(); private List<ChannelModel> channelModels; public static ChannelService getInstance() { return channelService; } /// <summary> /// 通道集合 /// </summary> public List<ChannelModel> ChannelModels { get { if (this.channelModels == null) { this.channelModels = new List<ChannelModel>(); } return this.channelModels; } set { this.channelModels = value; } } /// <summary> /// 獲取通道狀態 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <returns></returns> public static EChannelState GetChannelState(int channelType, int channelID) { return ChannelService.getInstance().ChannelModels.Find(c => Convert.ToInt32(c.ChannelType) == channelType && c.ChannelId == channelID).ChannelState; } /// <summary> /// 修改通道狀態 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> public static void UpdateChannelState(int channelType, int channelID, EChannelState channelState) { ChannelService.getInstance().ChannelModels.Find(c => Convert.ToInt32(c.ChannelType) == channelType && c.ChannelId == channelID).ChannelState = channelState; Console.WriteLine(string.Format("[修改了通道狀態]{0}" , ChannelService.getInstance().ChannelModels.Find(c => Convert.ToInt32(c.ChannelType) == channelType && c.ChannelId == channelID).ChannelState)); } /// <summary> /// 獲取通道 /// </summary> /// <param name="channelID"></param> /// <returns></returns> public static ChannelModel GetChannelById(int channelID) { return ChannelService.getInstance().ChannelModels.Find(c => c.ChannelId == channelID); } /// <summary> /// 獲取空閒通道 /// </summary> /// <returns></returns> public static ChannelModel GetChannelByIdle() { return ChannelService.getInstance().ChannelModels.Find(c => c.ChannelState == EChannelState.STATE_IDLE); } /// <summary> /// 獲取指定類型的通道數量 /// </summary> /// <param name="channelType"></param> /// <returns></returns> public static int GetChannelCount(int channelType) { return ChannelImport.tpi_GetChannelCount(channelType); } /// <summary> /// 獲取通道的時序,流號 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="pStream"></param> /// <param name="pTimeSlot"></param> /// <returns></returns> public static int GetChannelTimeSlot(int channelType, int channelID, out int pStream, out int pTimeSlot) { return ChannelImport.tpi_GetChannelTimeSlot(channelType, channelID, out pStream, out pTimeSlot); } /// <summary> /// 創建兩個通道間的雙向鏈接 /// </summary> /// <param name="destType"></param> /// <param name="destID"></param> /// <param name="srcType"></param> /// <param name="srcID"></param> /// <returns></returns> public static int TalkWith(int destType, int destID, int srcType, int srcID) { return ChannelImport.tpi_TalkWith(destType, destID, srcType, srcID); } /// <summary> /// 掛機 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="cause"></param> /// <returns></returns> public static int Hangup(int channelType, int channelID, int cause) { return CallImport.tpi_Hangup(channelType, channelID, cause); } }
public class EventService { private static void SetEventNotifyCallBackProc(ProcPhonicDTFireEventCallBack callBack) { EventImport.tpi_SetEventNotifyCallBackProc(callBack); } public static void InitCallBack() { //加載事件回調 SetEventNotifyCallBackProc(delegate(EPhonicEvent eventType, int channelType, int channelID, int iParam1, int iParam2) { Console.Write(eventType); switch (eventType) { case EPhonicEvent.eventState: OnState(channelType, channelID, iParam1, iParam2); break; case EPhonicEvent.eventDeviceTimer: OnTimer(channelType, channelID); break; case EPhonicEvent.eventSignal: //OnSignal(channelType, channelID, iParam1); break; case EPhonicEvent.eventAlarm: //OnAlarm(channelType, channelID, iParam1); break; case EPhonicEvent.eventIdle: OnIdle(channelType, channelID); break; case EPhonicEvent.eventCallIn: OnCallIn(channelType, channelID, new StringBuilder(iParam1.ToString()), new StringBuilder(iParam2.ToString())); break; case EPhonicEvent.eventAnswer: OnAnswer(channelType, channelID); break; case EPhonicEvent.eventCallOutFinish: OnCallOutFinish(channelType, channelID); break; case EPhonicEvent.eventCallFail: OnCallFail(channelType, channelID, iParam1); break; case EPhonicEvent.eventHangup: OnHangup(channelType, channelID, iParam1); break; case EPhonicEvent.eventDTMF: OnDTMF(channelType, channelID, iParam1); break; case EPhonicEvent.eventPlayEnd: OnPlayEnd(channelType, channelID, iParam1); break; case EPhonicEvent.eventRecordEnd: OnRecordEnd(channelType, channelID, iParam1); break; } }); } public static void OnState(int channelType, int channelID, int newChannelState, int oldChannelState) { Console.WriteLine(string.Format("[通道ID]{0} [新狀態]{1} [舊狀態]{2}", channelID, newChannelState, oldChannelState)); EChannelState channelState = (EChannelState)Enum.Parse(typeof(EChannelState), newChannelState.ToString()); //ChannelService.UpdateChannelState(channelType, channelID, channelState); ChannelModel channel = ChannelService.GetChannelById(channelID); channel.ChannelState = channelState; if (channelState == EChannelState.STATE_OUT_RELEASE) { channel.Destroy(); } } /// <summary> /// 通道定時 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> public static void OnTimer(int channelType, int channelID) { Console.WriteLine(string.Format("OnTimer [通道ID]{0}", channelID)); } /// <summary> /// 通道信令發生變化 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="Signal"></param> public static void OnSignal(int channelType, int channelID, int Signal) { Console.WriteLine(string.Format("OnSignal [通道ID]{0} [通道信令]{1}", channelID, Signal)); } /// <summary> /// 中繼告警 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="Alarm"></param> public static void OnAlarm(int channelType, int channelID, int Alarm) { Console.WriteLine(string.Format("OnAlarm [通道ID]{0} [告警值]{1}", channelID, Alarm)); } /// <summary> /// 通道進入空閒狀態 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> public static void OnIdle(int channelType, int channelID) { Console.WriteLine(string.Format("OnIdle [通道ID]{0}", channelID)); ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventIdle; channel.ChannelState = EChannelState.STATE_IDLE; } /// <summary> /// 通道有電話呼入 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="callerID"></param> /// <param name="phoneNumber"></param> public static void OnCallIn(int channelType, int channelID, StringBuilder callerID, StringBuilder phoneNumber) { Console.WriteLine(string.Format("OnCallIn [通道ID]{0} [主叫電話]{1} [被叫電話]{2}" , channelID, callerID.ToString(), phoneNumber.ToString())); //ChannelService.UpdateChannelState(channelType, channelID, EChannelState.STATE_IN_CALLING); ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventCallIn; channel.ChannelState = EChannelState.STATE_IN_CALLING; } /// <summary> /// 用戶已應答呼叫 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> public static void OnAnswer(int channelType, int channelID) { ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventAnswer; channel.ChannelState = EChannelState.STATE_IN_TALK; Console.WriteLine(string.Format("OnAnswer [通道ID]{0}", channelID)); } /// <summary> /// 對指定通道的呼叫已完成 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> public static void OnCallOutFinish(int channelType, int channelID) { ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventCallOutFinish; channel.ChannelState = EChannelState.STATE_OUT_RINGING; Console.WriteLine(string.Format("OnCallOutFinish [通道ID]{0}", channelID)); //ChannelService.UpdateChannelState(channelType, channelID, EChannelState.STATE_OUT_RINGING); } /// <summary> /// 對指定通道的呼叫失敗 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="cause"></param> public static void OnCallFail(int channelType, int channelID, int cause) { ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventCallFail; Console.WriteLine(string.Format("OnCallFail [通道ID]{0} [掛機緣由]{1}", channelID, cause)); Console.WriteLine(System.DateTime.Now.ToString()); } /// <summary> /// 通道已掛機 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="cause"></param> public static void OnHangup(int channelType, int channelID, int cause) { ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventHangup; channel.ChannelState = EChannelState.STATE_OUT_HANGUP; Console.WriteLine(string.Format("OnHangup [通道ID]{0} [掛機緣由]{1}" , channelID, cause)); //ChannelService.UpdateChannelState(channelType, channelID, EChannelState.STATE_OUT_HANGUP); } /// <summary> /// 用戶已按鍵 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="dtmfCode"></param> public static void OnDTMF(int channelType, int channelID, int dtmfCode) { ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventDTMF; Console.WriteLine(string.Format("OnDTMF [通道ID]{0} [用戶所按鍵的ASCII碼]{1}" , channelID, dtmfCode)); } /// <summary> /// 觸發信令跟蹤事件 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="signalType"></param> /// <param name="signalCode"></param> public static void OnSignalMonitor(int channelType, int channelID, int signalType, int signalCode) { Console.WriteLine(string.Format("OnSignalMonitor [通道ID]{0} [信令類型]{1} [信令碼]{2}" , channelID, signalType, signalCode)); } /// <summary> /// 對通道的放音已結束 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="completeSize"></param> public static void OnPlayEnd(int channelType, int channelID, int completeSize) { ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventPlayEnd; Console.WriteLine(string.Format("OnPlayEnd [通道ID]{0} [完成播放的字節數]{1}" , channelID, completeSize)); } /// <summary> /// 通道的錄音已結束 /// </summary> /// <param name="channelType"></param> /// <param name="channelID"></param> /// <param name="completeSize"></param> public static void OnRecordEnd(int channelType, int channelID, int completeSize) { ChannelModel channel = ChannelService.GetChannelById(channelID); channel.EventState = EPhonicEvent.eventRecordEnd; Console.WriteLine(string.Format("OnRecordEnd [通道ID]{0} [完成錄音的字節數]{1}" , channelID, completeSize)); } }
/// 打開和初始化設備 /// </summary> /// <returns>0 表示成功,其餘值表示出錯的錯誤碼,含義參看類型定義的EDtvcErrorCode 定義</returns> public static bool OpenDevice() { //詢問manage運行模式及運行狀態 //打開設備驅動 int isOpen = DeviceImport.tpi_OpenDevice(); if (isOpen != 0) { return false; } //獲取設備卡數量 int cardCount = CardService.GetCardCount(Convert.ToInt32(ECardType.CARD_TYPE_PCM)); if (cardCount == 0) { return false; } //初始化通道 int channelCount = ChannelService.GetChannelCount(Convert.ToInt32(EChannelType.CH_TRUNK)); for (int i = 0; i < channelCount - 1; i++) { ChannelModel channel = new ChannelModel() { ChannelId = i, ChannelState = EChannelState.STATE_IDLE, ChannelType = EChannelType.CH_TRUNK, GroupId = 0, StreamNo = 0, TimeSlot = 0, VoiceChannelAttrib = EVoiceChannelAttrib.ATTRIB_VOICE_PLAY_ONLY }; ChannelService.getInstance().ChannelModels.Add(channel); } //加載事件回調 EventService.InitCallBack(); return true; }
public class CallService { /// <summary> /// 在通道上創建任務呼叫 /// </summary> /// <param name="task"></param> public static ChannelModel TaskCall(TaskModel task) { ChannelModel channel = null; switch (task.TaskType) { case TaskType.voice: channel = VoiceCall(task); break; case TaskType.ansThr: break; case TaskType.key: break; case TaskType.keyThr: break; } return channel; } /// <summary> /// 純語音呼叫 /// </summary> private static ChannelModel VoiceCall(TaskModel task) { //獲取空閒通道 ChannelModel channel = ChannelService.GetChannelByIdle(); channel.Task = task; //創建呼叫 channel.MakeCall(true); //等待通道執行呼叫 while (channel.ChannelState != EChannelState.STATE_IDLE) { switch (channel.EventState) { case EPhonicEvent.eventAnswer: if (!channel.IsPlay) { Console.WriteLine(channel.IsPlay); channel.Play(); } break; case EPhonicEvent.eventHangup: if (channel.IsPlay) { channel.StopPlay(); } break; } } return channel; } private static void VoiceToCall() { ChannelModel channel = ChannelService.GetChannelByIdle(); channel.MakeCall(true); while (channel.ChannelState != EChannelState.STATE_OUT_HANGUP && channel.ChannelState != EChannelState.STATE_IN_CALLING) { } } }
private static ChannelModel SingleCall(bool isMultiplayer) { Console.WriteLine("請輸入電話號碼"); string phone = Console.ReadLine(); int channelType = 0; int pStream = 0; int pTimeSlot = 0; //ChannelService.GetChannelTimeSlot(channelType, channel.ChannelId, out pStream, out pTimeSlot); //Console.WriteLine(string.Format("通道[流號]{0},[時序]{1}", pStream, pTimeSlot)); Console.WriteLine("正在呼叫..."); TaskModel task = new TaskModel { TaskType = TaskType.voice, FromCall = "400*******", ToCall = phone, CallRingingTime = 10000 }; ChannelModel channel = CallService.TaskCall(task); Console.WriteLine(System.DateTime.Now.ToString()); //若是用戶沒掛機,強制掛機 if (!isMultiplayer) { //等待呼叫結束,獲取通道狀態 while (channel.ChannelState != EChannelState.STATE_IDLE) { } } return channel; }