知识大全 编程实现电话点歌-情人节特别礼物

Posted 情人

篇首语:胸怀万里世界, 放眼无限未来。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 编程实现电话点歌-情人节特别礼物相关的知识,希望对你有一定的参考价值。

  一 偶然的起因  记得还是在去年情人节的时候 当时一直在为给女朋友送什么礼物而发愁 觉得送花实在没有什么创意 可又不知道什么样的礼物即能给她一个惊喜同事又不昂贵 这时 我的一个好朋友出了一个主意 说不如电话点歌吧 还比较特别 可是如果是通过电台点歌后 再告诉她收听的话就起不到意外的效果了   就在没有什么好办法的时候 我在Delphi论坛上瞎逛的时候 一个人提出的问题突然启发了我 问题是关于如果编程实现语音留言和电话按键的记录功能的 我突然想为什么我不能写一个程序来控制电话 然后再给女友打一个传呼 让她回电话 当电话接通后 我的程序先播放一段事先录制好的话 提示她通过电话按键来选歌 并能提供留言的功能呢 主意一定 我就赶忙查阅这方面的资料了 一开始朋友们告诉可以通过语音卡来实现这些功能 可是语音卡比较贵 而且我买了后 除了用一次以外以后也不会经常用到 实在是有点浪费 后来网友cced提到他听人说TurboPower公司出的Async Professional控件提供了一组基于Telephone Api的控件可以通过语音Modem来实现类似的功能 这个看来成本就低多了 我的Modem正好是语音Modem 于是我就下载了Async Professional(官方网 turbopower )试验了一下 果然不同反响 便宜且简单

  二 开始设计  下面我们就来看看如何利用这组控件实现语音功能 对于我们程序的应用来说 只需要使用两个TAPI控件TApdComPort和TApdTapiDevice即可 其中TApdComPort控件是一个串口通讯控件 因为Modem是同串口相连接的 因此需要串口通讯控件来进行控制 而TapdTapiDevice则是提供语音功能的核心控件   首先 新建一个程序项目 在窗体上放置一个TApdComport控件 设置其属性为AutoOpen:=False;TapiMode=tmOn;这里TapiMode 设定为tmOn 表明TApdComPort 将由同其关联的TApdTapiDevice 控件来控制 而将AutoOpen设定为False 是因为串口的打开和关闭现在可以完全由TAPI来控制了   然后 在窗体上放置一个TApdTapiDevice控件 设定其Comport属性为前面的TApdComPort控件 设定AnswerOnRing属性为 表明第一次振铃后就开始由程序控制电话的应答 设定ShowTapiDevices为True表明当调用控件的SelectDevice方法时 会显示一个选择TAPI设备的对话框 ShowPorts属性为false 表明调用SelectDevice方法不会显示串行口列表   接下来 本程序主要是采用有限状态机制来控制流程的 下面我们来定义枚举状态

TypeTCurrentState = (csIdle csWaiting csConnected csPlaying csRecording sDisconnected);

  其中csIdle状态表示电话处于空闲状态 正等待接入 csWaiting则表示电话处于程序控制下 等待接入 如果有电话打入 程序会自动应答 csConnected则表示有电话打入 处于连接状态 csRecording则用来表示当前处于记录电话留言状态 csDisconnected则表示当前连接挂断了

  三 程序初始化  下面就是程序的OnCreate的事件处理函数 非常简单 就是先设置当前状态为csIdle 并设置ApdTapiDevice控件的TrimSeconds属性为 表示当录音时如果有5秒的沉默时间就挂断 procedure TFrmMain FormCreate(Sender: TObject);varTeleIni: TIniFile;beginCurrentState := csIdle;ApdTapiDevice TrimSeconds := ; //录音时有 秒静音就挂断

CommandList := TStringList Create;

TeleIni := TIniFile Create(ExtractFilePath(ParamStr( )) + Tele ini );TeleIni ReadSectionvalues( Commands CommandList);TeleIni Free;WindowState := wsMaximized;end;  然后是将定义在Tele Ini文件中的将要播放的声音列表文件目录加载到CommandList中 Tele Ini的示例如下 [Commands] #= wav #= wav #= wav #=E:\\Program Files\\APRO\\Examples\\Beep wav  其中 # 表示当用户按下 和#号按键后 程序会播放其对应的 wav文件 接下来就是我们要提供两个命令 一个是监控电话 一个是挂断电话 先在窗体上添加一个TlistBox 起名为LBSysInfo 然后添加两个菜单项 并同两个Action连接 编写Action的OnExecute事件处理函数

//监控电话procedure TFrmMain ActionAnswerExecute(Sender: TObject);begintryApdTapiDevice EnableVoice := True;exceptApplication MessageBox( 当前设备不支持语音扩展 错误 MB_OK);end;

if ApdTapiDevice EnableVoice thenbeginApdTapiDevice AutoAnswer;LBSysInfo Items Add( answer:接听对方电话 );CurrentState := csWaiting;endend;

  因为不是所有的Modem都支持语音功能 因此在监控电话接入前应该先判断设置ApdTapiDevice EnableVoice := True; 如果出现异常 表明Modem不支持语音功能 如果支持的话 就调用AutoAnswer方法等待接入同时设置状态为csWaiting 并在列表框中写入日志

//挂断电话procedure TFrmMain ActionCancelExecute(Sender: TObject);beginApdTapiDevice CancelCall;LBSysInfo Items Add( cancel:挂断对方电话 );CurrentState := csIdle;end;

  挂断电话就简单多了 只要简单的调用TApdTapiDevice控件的CancelCall方法就可以了 还需要设置当前状态为csIdle

  如果系统中存在多个TAPI设备的时候 我们还可以选择使用哪一个来接听电话 下面是选择设备的方法

//选择设备procedure TFrmMain ActionSelDevExecute(Sender: TObject);beginApdTapiDevice SelectDevice;ApdTapiDevice EnableVoice := True;end;

事件驱动Telephone API是基于事件驱动的 因此核心功能需要在事件处理函数中实现 先来看程序的TApdTapiDevice的OnConnect事件处理函数代码

procedure TFrmMain ApdTapiDeviceTapiConnect(Sender: TObject);beginCurrentState := csConnected;LBSysInfo Items Add( Connect:连接成功 );ApdTapiDevice PlayWaveFile( Greeting wav );//播放功能提示语音LBSysInfo Items Add( connect:播放greeting wav );end;

  当用户打入被监控的电话后 会激发这个事件 程序应该在用户接入后播放提示语音 提示用户按不同功能键来点歌或留言 程序设置当前状态为csConnected 然后调用ApdTapiDevice的PlayWaveFile方法播放提示语音波文件   要注意的是 不同Modem支持播放的波文件的格式是不同的 但它们都支持PCM 位单声道的波文件 但这种类型波文件的音质非常差 用来播放歌曲效果实在糟糕 不过大多数语音Modem都支持音质更好的波文件格式 但通常都是 PCM格式的 比如我的Lucent Voice Modem就支持PCM 位单声道的波文件的播放 歌曲转化为波文件非常简单 我用Winamp将mp 文件通过Winamp本身的Disk Writer Plug in插件直接将mp 转化成 位的波文件(通常为 M大小) 然后再用一个叫goldwave的软件(我忘了从什么地方下载的了)将其转化为 位的单声道波文件(通常 M大小) 至于提示语音 我则是使用windows自带的录音机程序通过麦克风录制的   当用户听完提示语音后 他们会按键来点歌或留言 而用户的按键会激发TApdTapiDevice的OnDTMF事件 我们就可以在这个事件中对按键进行处理 下面就是处理过程代码

procedure TFrmMain ApdTapiDeviceTapiDTMF(CP: TObject; Digit: Char;ErrorCode: Integer);beginif (Digit = ) or (Digit = ) thenExit;LBSysInfo Items Add( dtmf:按键= + Digit);

CurrentCommand := CurrentCommand + Digit;简单状态机if Digit = # thenbeginif CurrentCommand = *# thenbeginCurrentCommand := ;ApdTapiDevice MaxMessageLength := ; //最长记录时间 秒ApdTapiDevice InterruptWave := False; //按键不能中断提示语音的播放 ApdTapiDevice PlayWaveFile( recordhint wav );//播放录音提示语音CurrentState := csRecording;Exit;end;

if CommandList values[CurrentCommand] <> thenbeginApdTapiDevice PlayWaveFile(CommandList values[CurrentCommand]);LBSysInfo Items Add(Format( %s %s 正在播放 %s [ApdTapiDevice calleridname apdtapidevice callerid CommandList values[CurrentCommand]]));endelsebegin//播放错误提示语音 并要求用户重新输入命令ApdTapiDevice PlayWaveFile( errorno wav );LBSysInfo Items Add(Format( %s %s 输入了错误的号码 [ApdTapiDevice calleridname apdtapidevice callerid]));end;//重置命令为空CurrentCommand := ;end;end;

  程序对按键进行判断(按键对应于digit参数) 如果输入的为 *# 键 就进入录音功能 在录音前先播放提示语音 可以告诉用户留言长度为 秒 然后设置当前状态为csRecording 有人可能要问 没看到用来录音的代码呀 这部分其实是实现在另外的事件中的 我们稍后就会讲到 再来看点歌部分 同样的根据按键的组合在先前加载进CommandList的字符串列表中查找相匹配的歌曲 如果有相应的歌曲就播放 否则播放错误提示语音 提示用户重新输入命令 然后将按键清空等待重新输入 另外注意在事件的日志记录中我记录了ApdTapiDevice calleridname和CallerID的属性 它们对应的是打入电话的号码 不过这项功能只对开通了来电显示功能的电话号码才有效 通过对打入电话号码信息的处理 我们可以提供一些额外的功能 不过这是题外话了   前面提到了在按键处理事件中我们并没有进行留言的录制功能 这主要是因为我们要保证留言提示语音不被按键中断(设定Interrupave:=false) 因此把留言录制功能放到了TApdTapiDevice的OnWaveNotify事件中了 这个事件可以提示波文件播放的状态 比如播放结束和录音所需声音数据准备状态等 在本程序中我们需要在提示语音播放结束后 开始记录留言 并在留言声音数据准备好后 将其保存到磁盘文件中 下面是处理过程的流程

procedure TFrmMain ApdTapiDeviceTapiWaveNotify(CP: TObject;Msg: aveMessage);varTimeStr: string;FileName: string;begin//决不能在case外做耗时的操作case Msg ofwaPlayOpen: LBSysInfo Items Add( wavnotify:播放开始 );waPlayDone:beginLBSysInfo Items Add( wavnotify:播放结束 );if CurrentState = csRecording thenbegintry      //等待波设备状态为wsIdle再开始录音while ApdTapiDevice WaveState <> wsIdle doApplication ProcessMessages; ApdTapiDevice InterruptWave := True;ApdTapiDevice StartWaveRecord;LBSysInfo Items Add( dtmf:录音成功 );exceptLBSysInfo Items Add( dtmf:录音失败 );end;end;end;waPlayClose: LBSysInfo Items Add( wavnotify:播放关闭 );waRecordOpen: LBSysInfo Items Add( wavnotify:录音开始 );waDataReady:beginLBSysInfo Items Add( wavnotify:数据准备 );TimeSeparator := ;FileName := DateTimeToStr(Now) + wav ;tryApdTapiDevice SaveWaveFile(ExtractFilePath(ParamStr( )) + record\\ +FileName True);LBSysInfo Items Add( wavNotify:保存声音文件 + FileName);exceptLBSysInfo Items Add( wavnotify:保存声音文件失败 );end;end;waRecordClose:beginLBSysInfo Items Add( wavnotify:记录声音结束 );CurrentState := csWaiting;ActionCancelExecute(nil);Timer Enabled := True;end;end;end;

  整个流程就是通过一个Case语句来判断当前声音状态 如果为waPlayDone(播放完毕) 同时CurrentStatus为csRecording的话 就调用StartWaveRecord方法来记录声音 而当Msg为waDataReady状态时 表明录音数据已经可以存盘了 这时根据当前时间生成一个文件名 并将数据保存为波文件 而当录音结束后 我们就需要调用ActionCancelExecute(nil)来挂断电话 并将状态设置为csWaiting来等待下次接入 注意在代码最后 我们将一个TTimer控件激活了 这个TTimer控件的时间间隔Interval设置为 秒 同时其OnTimer事件代码如下

procedure TFrmMain Timer Timer(Sender: TObject);begintry  //应答电话ActionAnswerExecute(nil);CurrentState := csWaiting;Timer Enabled := False;exceptend;end;

  这样设置的原因在于 当调用CancelCall方法来挂断电话后 TAPI设备需要 秒来恢复正常状态 如果立刻执行AutoAnswer的话 这个方法就会失效 无法正确监控电话接入 因此要用TTimer来控制恢复电话应答的时间

  四 异常处理  要想程序非常健壮的反复应答电话接入 我们必须对用户突然挂断电话进行处理 用户断开的事件会激发控件的OnTapiStatus事件 当用户挂断电话时 我们要做的是如果当前还在录音 就停止录音 如果是在播放歌曲 就挂断电话 然后设置TTimer生效 重新进入电话应答状态 下面就是整个处理过程的代码

procedure TFrmMain ApdTapiDeviceTapiStatus(CP: TObject; First Last: Boolean; Device Message Param Param Param : Cardinal);beginif (Message = Line_CallState) thenbegincase Param ofLineCallState_Disconnected:beginLBSysInfo Items Add( status:disconnected from remote modem );if CurrentState = csRecording thenbeginApdTapiDevice StopWaveRecord;Exit;end;CurrentState := csDisconnected;ActionCancelExecute(nil);Timer Enabled := True;end;end;end;end;

  五 进一步完善  当录音完毕后 我们想听一下电话留言的话 可以在窗体上放置一个打开文件对话框 用下面代码实现

procedure TFrmMain ActionPlayRecExecute(Sender: TObject);varFrmPlay: TFrmPlayRec;beginDlgOpenRec InitialDir := ExtractFilePath(ParamStr( )) + Record\\ ;if DlgOpenRec Execute then//播放声音记录文件ShellExecute(Application Handle PChar( open ) PChar(DlgOpenRec FileName) nil nil SW_SHOW);end;

cha138/Article/program/Delphi/201311/8505

相关参考

知识大全 七夕情人节送什么礼物给妈妈

七夕情人节送什么礼物给妈妈妈妈最喜欢什么就送什么呗~也不需要特别贵的,表示心意就行~~七夕情人节送妈妈什么礼物好可以给妈妈买一下小饰品。或者帮妈妈做一顿晚饭(如果你还小,可以先帮忙。做一些力所能及的小

知识大全 七夕情人节礼物想送点特别的

七夕情人节礼物想送点特别的其实男人都比较理性,送给男士的礼品不用特别,最重要的是给男士他需要的礼品。因为之前一直都在做礼品推荐方案,所以总结出来一些为经验,其实送男士的话不同的人有不同的需要,给一些建

知识大全 七夕情人节送什么礼物好 diy七夕礼物推荐

七夕情人节送什么礼物好diy七夕礼物推荐七夕节送什么礼物好?相信这是很多青年男女在七夕前最纠结的问题,七夕情人节送什么好?其实只要是自己用心准备的礼物,送什么都是好的,如果七夕情人节不知道送什么,不妨

知识大全 情人节新意diy礼物

情人节新意diy礼物1.自己做巧克力2.去地王广场自己买银粘土做银戒指3.织围巾4.录一段videodiy情人节礼物,手工diy情人节礼物手工diy情人节礼物,比如T恤、内裤定做签名。源自法国的亲密男

知识大全 情人节送女友的礼物什么好

情人节送女友的礼物什么好?情人节送什么礼物比较有心意?其实只要是楼主你用心去选的礼物,都很有心意啊。在选礼物之前你要先清楚你女朋友喜欢什么,根据她的喜好选择的礼物一方面她肯定会很开心,另一方面也是楼主

知识大全 情人节送什么DIY礼物好

情人节送什么DIY礼物好  以下文字资料是由(本站网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!情人节送什么DIY礼物好曾经有

知识大全 七夕情人节送什么礼物给女孩子好

七夕情人节送什么礼物给女孩子好七夕情人节可以给女孩子送下面这些礼物:(1)玫瑰,表示爱意;(2)巧克力,希望两人的感情一直甜甜蜜蜜;(3)零食,女孩大多爱吃零食;(4)微信红包,最实在的礼物;(5)项

知识大全 ◆◇大大们:情人节送什么礼物

◆◇大大们:情人节送什么礼物?要有心意/新意,要不落俗套,既在情理之中,又在意料之外。◇◆14号情人(星期六)节快到了,首先希望都能要好好过,也祝您幸福.令人饱含期待的情人节即将来临,梦一般的缤纷色彩

知识大全 七夕情人节刚好是女友的生日,又是情人节,送女朋友什么生日礼物最好最有意义

七夕情人节刚好是女友的生日,又是情人节,送女朋友什么生日礼物最好最有意义?七夕情人节刚好是女友的生日,又是情人节,送女朋友什么生日礼物最好最有意义?答:女朋友过生日送什么礼物浪漫?七夕情人节要让她感受

知识大全 七夕情人节的时候男朋友生日送什么礼物好

七夕情人节的时候男朋友生日送什么礼物好?男人也喜欢浪漫,玫瑰必不可少呀!roseonly是最好的选择,玫瑰,饱满、娇艳、储存期又长,每一朵都像刚摘下来时一样娇艳。七夕情人节临近,给男朋友送什么生日礼物