Changed for compatibility with older GNU compilers
[openh323.git] / src / opalvxml.cxx
blob779a07fef601fdcdab4223977d5d81fa7c111fdc
1 /*
2 * vxml.cxx
4 * VXML control for for OpenIVR
6 * A H.323 IVR application.
8 * Copyright (C) 2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
27 * Revision 1.5 2002/07/03 04:58:13 robertj
28 * Changed for compatibility with older GNU compilers
30 * Revision 1.4 2002/07/02 06:32:51 craigs
31 * Added recording functions
33 * Revision 1.3 2002/06/28 02:42:11 craigs
34 * Fixed problem with G.723.1 file naming conventions
35 * Fixed problem with incorrect file open mode
37 * Revision 1.2 2002/06/28 01:24:03 robertj
38 * Fixe dproblem with compiling without expat library.
40 * Revision 1.1 2002/06/27 05:44:11 craigs
41 * Initial version
43 * Revision 1.2 2002/06/26 09:05:28 csoutheren
44 * Added ability to utter various "sayas" types within prompts
46 * Revision 1.1 2002/06/26 01:13:53 csoutheren
47 * Disassociated VXML and Opal/OpenH323 specific elements
49 * Revision 1.2 2002/06/21 08:18:22 csoutheren
50 * Added start of grammar handling
52 * Revision 1.1 2002/06/20 06:35:44 csoutheren
53 * Initial version
57 #include <ptlib.h>
59 #if P_EXPAT
61 #include <lpc10codec.h>
62 #include <mscodecs.h>
63 #include <gsmcodec.h>
64 #include <ptclib/delaychan.h>
65 #include <ptclib/pwavfile.h>
66 #include <ptclib/memfile.h>
68 #include "opalvxml.h"
70 #define G7231_SAMPLES_PER_BLOCK 240
71 #define G7231_BANDWIDTH (6300/100)
74 ///////////////////////////////////////////////////////////////
76 OpalVXMLSession::OpalVXMLSession()
78 incomingChannel = NULL;
79 outgoingChannel = NULL;
82 PVXMLIncomingChannel * OpalVXMLSession::CreateIncomingAudioChannel(H323AudioCodec & codec)
84 BOOL mediaIsPCM = FALSE;
85 OpalMediaFormat mediaFormat = codec.GetMediaFormat();
87 if (!codec.IsRawDataChannelNative())
88 mediaIsPCM = TRUE;
89 else if (mediaFormat == OPAL_G7231_6k3)
90 mediaIsPCM = FALSE;
91 else {
92 PTRACE(2, "OpenIVR\tUnknown media format " << mediaFormat);
93 return NULL;
96 if (mediaIsPCM)
97 return SetIncomingAudioChannel(new OpalVXMLIncomingChannelPCM(*this));
99 return SetIncomingAudioChannel(new OpalVXMLIncomingChannelG7231(*this));
102 PVXMLOutgoingChannel * OpalVXMLSession::CreateOutgoingAudioChannel(H323AudioCodec & codec)
104 BOOL mediaIsPCM = FALSE;
105 OpalMediaFormat mediaFormat = codec.GetMediaFormat();
107 if (!codec.IsRawDataChannelNative())
108 mediaIsPCM = TRUE;
109 else if (mediaFormat == OPAL_G7231_6k3)
110 mediaIsPCM = FALSE;
111 else {
112 PTRACE(2, "OpenIVR\tUnknown media format " << mediaFormat);
113 return NULL;
116 if (mediaIsPCM)
117 return SetOutgoingAudioChannel(new OpalVXMLOutgoingChannelPCM(*this));
119 return SetOutgoingAudioChannel(new OpalVXMLOutgoingChannelG7231(*this));
122 PVXMLIncomingChannel * OpalVXMLSession::SetIncomingAudioChannel(PVXMLIncomingChannel * chan)
124 if (incomingChannel != NULL) {
125 delete incomingChannel;
126 incomingChannel = NULL;
129 incomingChannel = chan;
130 return incomingChannel;
133 PVXMLOutgoingChannel * OpalVXMLSession::SetOutgoingAudioChannel(PVXMLOutgoingChannel * chan)
135 if (outgoingChannel != NULL) {
136 delete outgoingChannel;
137 outgoingChannel = NULL;
140 outgoingChannel = chan;
141 return outgoingChannel;
144 BOOL OpalVXMLSession::PlayFile(const PString & fn)
146 if (outgoingChannel != NULL)
147 outgoingChannel->QueueFile(fn);
149 return TRUE;
152 BOOL OpalVXMLSession::PlayData(const PBYTEArray & data)
154 if (outgoingChannel != NULL)
155 outgoingChannel->QueueData(data);
157 return TRUE;
160 BOOL OpalVXMLSession::IsPlaying() const
162 return (outgoingChannel != NULL) && outgoingChannel->IsPlaying();
165 BOOL OpalVXMLSession::StartRecording(const PFilePath & fn)
167 if (incomingChannel != NULL)
168 return incomingChannel->StartRecording(fn);
170 return FALSE;
173 BOOL OpalVXMLSession::EndRecording()
175 if (incomingChannel != NULL)
176 return incomingChannel->EndRecording();
178 return FALSE;
181 BOOL OpalVXMLSession::IsRecording() const
183 return (incomingChannel != NULL) && incomingChannel->IsRecording();
186 ///////////////////////////////////////////////////////////////
188 BOOL CheckWAVFileValid(OpalWAVFile & chan, BOOL mustBePCM)
190 // Check the wave file header
191 if (!chan.IsValid()) {
192 PTRACE(1, chan.GetName() << " wav file header invalid");
193 return FALSE;
196 // Check the wave file format
197 if (mustBePCM) {
198 if (chan.GetFormat() != PWAVFile::fmt_PCM) {
199 PTRACE(1, chan.GetName() << " is not a PCM format wav file");
200 PTRACE(1, "It is format " << chan.GetFormat() );
201 return FALSE;
204 if ((chan.GetSampleRate() != 8000) &&
205 (chan.GetChannels() != 1) &&
206 (chan.GetSampleSize() != 16)) {
207 PTRACE(1, chan.GetName() << " is not a 16 Bit, Mono, 8000 Hz (8Khz) PCM wav file");
208 PTRACE(1, "It is " << chan.GetSampleSize() << " bits, "
209 << (chan.GetChannels()==1 ? "mono " : "stereo ")
210 << chan.GetSampleRate() << " Hz");
211 return FALSE;
215 else if ((chan.GetFormat() != PWAVFile::fmt_MSG7231) &&
216 (chan.GetFormat() != PWAVFile::fmt_VivoG7231)) {
217 PTRACE(1, chan.GetName() << " is not a G.723.1 format wav file");
218 PTRACE(1, "It is format " << chan.GetFormat() );
219 return FALSE;
222 return TRUE;
225 ///////////////////////////////////////////////////////////////
227 OpalVXMLIncomingChannel::OpalVXMLIncomingChannel(OpalVXMLSession & vxml)
228 : PVXMLIncomingChannel(vxml)
232 PWAVFile * OpalVXMLIncomingChannel::CreateWAVFile(const PFilePath & fn)
234 return new OpalWAVFile(fn, PFile::WriteOnly, PFile::ModeDefault, GetWavFileType());
237 ///////////////////////////////////////////////////////////////
239 OpalVXMLIncomingChannelPCM::OpalVXMLIncomingChannelPCM(OpalVXMLSession & vxml)
240 : OpalVXMLIncomingChannel(vxml)
244 BOOL OpalVXMLIncomingChannelPCM::WriteFrame(const void * buf, PINDEX len)
246 //cerr << "Writing PCM " << len << endl;
247 return wavFile->Write(buf, len);
250 void OpalVXMLIncomingChannelPCM::DelayFrame(PINDEX len)
252 delay.Delay(len/16);
255 ///////////////////////////////////////////////////////////////
256 // Override some of the IncomingChannelPCM functions to write
257 // G723.1 data instead of PCM data.
259 OpalVXMLIncomingChannelG7231::OpalVXMLIncomingChannelG7231(OpalVXMLSession & vxml)
260 : OpalVXMLIncomingChannel(vxml)
264 BOOL OpalVXMLIncomingChannelG7231::WriteFrame(const void * buf, PINDEX /*len*/)
266 int frameLen = G7231_File_Codec::GetFrameLen(*(BYTE *)buf);
267 return wavFile->Write(buf, frameLen);
270 void OpalVXMLIncomingChannelG7231::DelayFrame(PINDEX /*len*/)
272 // Ignore the len parameter as that is the compressed size.
273 // We must delay by the actual sample time.
274 delay.Delay((G7231_SAMPLES_PER_BLOCK*2)/16);
277 ///////////////////////////////////////////////////////////////
279 OpalVXMLOutgoingChannel::OpalVXMLOutgoingChannel(OpalVXMLSession & _vxml)
280 : PVXMLOutgoingChannel(_vxml)
284 PWAVFile * OpalVXMLOutgoingChannel::CreateWAVFile(const PFilePath & fn)
286 OpalWAVFile * file = new OpalWAVFile(fn, PFile::ReadOnly, PFile::ModeDefault, GetWavFileType());
287 if (!IsWAVFileValid(*file)) {
288 delete file;
289 file = NULL;
292 return file;
295 ///////////////////////////////////////////////////////////////
297 OpalVXMLOutgoingChannelPCM::OpalVXMLOutgoingChannelPCM(OpalVXMLSession & vxml)
298 : OpalVXMLOutgoingChannel(vxml)
302 BOOL OpalVXMLOutgoingChannelPCM::IsWAVFileValid(OpalWAVFile & chan)
304 return CheckWAVFileValid(chan, TRUE);
307 void OpalVXMLOutgoingChannelPCM::DelayFrame(PINDEX amount)
309 delay.Delay(amount / 16);
312 BOOL OpalVXMLOutgoingChannelPCM::ReadFrame(PINDEX amount)
314 frameOffs = 0;
315 frameLen = amount;
317 BOOL result = PIndirectChannel::Read(frameBuffer.GetPointer(), frameLen);
319 // if we did not read a full frame of audio, fill the end of the
320 // frame with zeros.
321 PINDEX count = GetLastReadCount();
322 if (count < frameLen)
323 memset(frameBuffer.GetPointer()+count, 0, frameLen-count);
325 return result;
328 void OpalVXMLOutgoingChannelPCM::CreateSilenceFrame(PINDEX amount)
330 frameOffs = 0;
331 frameLen = amount;
332 memset(frameBuffer.GetPointer(), 0, frameLen);
335 ///////////////////////////////////////////////////////////////
337 OpalVXMLOutgoingChannelG7231::OpalVXMLOutgoingChannelG7231(OpalVXMLSession & vxml)
338 : OpalVXMLOutgoingChannel(vxml)
342 void OpalVXMLOutgoingChannelG7231::QueueFile(const PString & ofn)
344 PString fn = ofn;;
346 // add in _g7231 prefix, if not there already
347 PINDEX pos = ofn.FindLast('.');
348 if (pos == P_MAX_INDEX) {
349 if (fn.Right(6) != "_g7231")
350 fn += "_g7231";
351 } else {
352 PString basename = ofn.Left(pos);
353 PString ext = ofn.Mid(pos+1);
354 if (basename.Right(6) != "_g7231")
355 basename += "_g7231";
356 fn = basename + "." + ext;
358 PVXMLOutgoingChannel::QueueFile(fn);
361 BOOL OpalVXMLOutgoingChannelG7231::IsWAVFileValid(OpalWAVFile & chan)
363 return CheckWAVFileValid(chan, FALSE);
366 void OpalVXMLOutgoingChannelG7231::DelayFrame(PINDEX /*amount*/)
368 delay.Delay(30);
371 BOOL OpalVXMLOutgoingChannelG7231::ReadFrame(PINDEX /*amount*/)
373 if (!PIndirectChannel::Read(frameBuffer.GetPointer(), 1))
374 return FALSE;
376 frameOffs = 0;
377 frameLen = G7231_File_Codec::GetFrameLen(frameBuffer[0]);
379 return PIndirectChannel::Read(frameBuffer.GetPointer()+1, frameLen-1);
382 void OpalVXMLOutgoingChannelG7231::CreateSilenceFrame(PINDEX /*amount*/)
384 frameOffs = 0;
385 frameLen = 4;
387 frameBuffer[0] = 2;
388 memset(frameBuffer.GetPointer()+1, 0, 3);
391 ///////////////////////////////////////////////////////////////
392 // : H323AudioCapability(12*G7231_BLOCK_SIZE, 4*G7231_BLOCK_SIZE)
394 G7231_File_Capability::G7231_File_Capability()
395 : H323AudioCapability(8, 4)
399 BOOL G7231_File_Capability::OnSendingPDU(H245_AudioCapability & cap, unsigned packetSize) const
401 // set the choice to the correct type
402 cap.SetTag(GetSubType());
404 // get choice data
405 H245_AudioCapability_g7231 & g7231 = cap;
407 // max number of audio frames per PDU we want to send
408 g7231.m_maxAl_sduAudioFrames = packetSize;
410 // no silence suppression
411 g7231.m_silenceSuppression = FALSE;
413 return TRUE;
416 BOOL G7231_File_Capability::OnReceivedPDU(const H245_AudioCapability & cap, unsigned & packetSize)
418 const H245_AudioCapability_g7231 & g7231 = cap;
419 packetSize = g7231.m_maxAl_sduAudioFrames;
420 return TRUE;
423 PObject * G7231_File_Capability::Clone() const
425 return new G7231_File_Capability(*this);
428 H323Codec * G7231_File_Capability::CreateCodec(H323Codec::Direction direction) const
430 return new G7231_File_Codec(direction);
433 ///////////////////////////////////////////////////////////////
435 G7231_File_Codec::G7231_File_Codec(Direction dir)
436 : H323AudioCodec(OPAL_G7231_6k3, dir)
441 int G7231_File_Codec::GetFrameLen(int val)
443 static const int frameLen[] = { 24, 20, 4, 1 };
444 return frameLen[val & 3];
447 BOOL G7231_File_Codec::Read(BYTE * buffer, unsigned & length, RTP_DataFrame &)
449 if (rawDataChannel == NULL)
450 return FALSE;
452 if (!rawDataChannel->Read(buffer, 1)) {
453 cerr << "Read failed" << endl;
454 return FALSE;
456 if (rawDataChannel->GetLastReadCount() == 0) {
457 cerr << "Read count is zero" << endl;
458 return FALSE;
461 int readLen = G7231_File_Codec::GetFrameLen(buffer[0]);
463 if (readLen > 0) {
464 if (!rawDataChannel->Read(buffer+1, readLen-1)) {
465 cerr << "Read failed" << endl;
466 return FALSE;
468 if (rawDataChannel->GetLastReadCount() == 0) {
469 cerr << "Read count is zero" << endl;
470 return FALSE;
474 length = readLen;
475 return TRUE;
478 BOOL G7231_File_Codec::Write(const BYTE * buffer, unsigned length, const RTP_DataFrame & /* rtp */, unsigned & writtenLength)
480 if (rawDataChannel == NULL)
481 return TRUE;
483 static const BYTE silence[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
484 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
485 0, 0, 0, 0};
487 // If the length is zero, output silence to the file..
488 if (length == 0) {
489 PTRACE(1,"G.723.1 zero length frame");
490 writtenLength = 0;
491 return rawDataChannel->Write(silence, 24);
494 int writeLen;
495 switch (buffer[0]&3) {
496 case 0:
497 writeLen = 24;
498 break;
499 case 1:
500 writeLen = 20;
501 break;
502 case 2:
503 // Windows Media Player cannot play 4 byte SID (silence) frames.
504 // So write out a 24 byte frame of silence instead.
505 PTRACE(1,"Replacing SID with 24 byte frame");
506 writtenLength = 4;
507 return rawDataChannel->Write(silence, 24);
508 // writeLen = 4;
509 // break;
510 default :
511 writeLen = 1;
512 break;
515 PTRACE(1,"G.723.1 frame length = " <<writeLen);
517 writtenLength = writeLen;
519 return rawDataChannel->Write(buffer, writeLen);
522 unsigned G7231_File_Codec::GetBandwidth() const
524 return G7231_BANDWIDTH;
527 //////////////////////////////////////////////////////////////////
529 PVXMLOutgoingChannel::PVXMLOutgoingChannel(PVXMLSession & _vxml)
530 : PVXMLChannel(_vxml)
532 playing = FALSE;
533 frameLen = frameOffs = 0;
534 silentCount = 20; // wait 20 frames before playing the OGM
537 BOOL PVXMLOutgoingChannel::AdjustFrame(void * buffer, PINDEX amount)
539 if ((frameOffs + amount) > frameLen) {
540 cerr << "Reading past end of frame:offs=" << frameOffs << ",amt=" << amount << ",len=" << frameLen << endl;
541 return TRUE;
543 //PAssert((frameOffs + amount) <= frameLen, "Reading past end of frame");
545 memcpy(buffer, frameBuffer.GetPointer()+frameOffs, amount);
546 frameOffs += amount;
548 lastReadCount = amount;
550 return frameOffs == frameLen;
553 void PVXMLOutgoingChannel::QueueFile(const PString & fn)
555 PWaitAndSignal mutex(vxmlMutex);
556 PTRACE(1, "Enqueueing file " << fn << " for playing");
557 playQueue.Enqueue(new PVXMLQueueFilenameItem(fn));
560 void PVXMLOutgoingChannel::QueueData(const PBYTEArray & data)
562 PWaitAndSignal mutex(vxmlMutex);
563 PTRACE(1, "Enqueueing " << data.GetSize() << " bytes for playing");
564 playQueue.Enqueue(new PVXMLQueueDataItem(data));
567 void PVXMLOutgoingChannel::PlayFile(PFile * chan)
569 PWaitAndSignal mutex(vxmlMutex);
571 if (!chan->Open(PFile::ReadOnly)) {
572 PTRACE(1, "Cannot open file \"" << chan->GetName() << "\"");
573 return;
576 PTRACE(1, "Playing file \"" << chan->GetName() << "\"");
577 totalData = 0;
578 SetReadChannel(chan, TRUE);
581 void PVXMLOutgoingChannel::FlushQueue()
583 PWaitAndSignal mutex(vxmlMutex);
585 if (GetBaseReadChannel() != NULL)
586 PIndirectChannel::Close();
588 PVXMLQueueItem * qItem;
589 while ((qItem = playQueue.Dequeue()) != NULL)
590 delete qItem;
593 BOOL PVXMLOutgoingChannel::Read(void * buffer, PINDEX amount)
595 PWaitAndSignal mutex(vxmlMutex);
597 // Create the frame buffer using the amount of bytes the codec wants to
598 // read. Different codecs use different read sizes.
599 frameBuffer.SetMinSize(1024); //amount);
601 // assume we are returning silence
602 BOOL doSilence = TRUE;
603 BOOL frameBoundary = FALSE;
605 // if still outputting a frame from last time, then keep doing it
606 if (frameOffs < frameLen) {
608 frameBoundary = AdjustFrame(buffer, amount);
609 doSilence = FALSE;
611 } else {
613 // if we are returning silence frames, then decrement the frame count
614 if (silentCount > 0)
615 silentCount--;
617 // if a channel is already open, don't do silence
618 else if (GetBaseReadChannel() != NULL)
619 doSilence = FALSE;
621 // If not in silence and no existing channel, open a new file.
622 else {
623 PVXMLQueueItem * qItem= playQueue.Dequeue();
624 if (qItem != NULL) {
625 qItem->Play(*this);
626 doSilence = FALSE;
627 totalData = 0;
628 playing = TRUE;
629 delete qItem;
634 // if not doing silence, try and read from the file
635 if (!doSilence) {
637 if (ReadFrame(amount)) {
638 frameBoundary = AdjustFrame(buffer, amount);
639 totalData += amount;
641 } else {
643 playing = FALSE;
645 PTRACE(1, "Finished playing " << totalData << " bytes");
646 PIndirectChannel::Close();
648 // if nothing left in the play queue, re-execute VXML script
649 if (playQueue.GetSize() == 0)
650 vxml.Execute();
652 silentCount = 5; // always do 5 frames of silence after every file
654 // no silence
655 doSilence = TRUE;
660 // start silence frame if required
661 if (doSilence) {
662 CreateSilenceFrame(amount);
663 frameBoundary = AdjustFrame(buffer, amount);
666 // delay to synchronise to frame boundary
667 if (frameBoundary)
668 DelayFrame(amount);
670 return TRUE;
673 BOOL PVXMLOutgoingChannel::Close()
675 PWaitAndSignal mutex(vxmlMutex);
676 PIndirectChannel::Close();
677 return TRUE;
680 ///////////////////////////////////////////////////////////////
682 void PVXMLQueueFilenameItem::Play(PVXMLOutgoingChannel & outgoingChannel)
684 // check the file extension and open a .wav or a raw (.sw or .g723) file
685 if ((fn.Right(4)).ToLower() == ".wav") {
686 PWAVFile * chan = outgoingChannel.CreateWAVFile(fn);
687 if (chan == NULL) {
688 PTRACE(1, "Cannot create WAV file");
690 else if (!chan->IsOpen()) {
691 PTRACE(1, "Cannot open file \"" << chan->GetName() << '"');
692 delete chan;
693 } else {
694 PTRACE(1, "Playing file \"" << chan->GetName() << "\"");
695 outgoingChannel.SetReadChannel(chan, TRUE);
697 } else { // raw file (eg .sw)
698 PFile *chan;
699 chan = new PFile(fn);
700 if (!chan->Open(PFile::ReadOnly)) {
701 PTRACE(1, "Cannot open file \"" << chan->GetName() << "\"");
702 delete chan;
703 } else {
704 PTRACE(1, "Playing file \"" << chan->GetName() << "\"");
705 outgoingChannel.SetReadChannel(chan, TRUE);
710 ///////////////////////////////////////////////////////////////
712 void PVXMLQueueDataItem::Play(PVXMLOutgoingChannel & outgoingChannel)
714 PMemoryFile * chan = new PMemoryFile(data);
715 PTRACE(1, "Playing " << data.GetSize() << " bytes");
716 outgoingChannel.SetReadChannel(chan, TRUE);
719 ///////////////////////////////////////////////////////////////
721 PVXMLIncomingChannel::PVXMLIncomingChannel(PVXMLSession & _vxml)
722 : PVXMLChannel(_vxml)
724 wavFile = NULL;
727 PVXMLIncomingChannel::~PVXMLIncomingChannel()
729 EndRecording();
732 BOOL PVXMLIncomingChannel::Write(const void * buf, PINDEX len)
734 PWaitAndSignal mutex(vxmlMutex);
736 DelayFrame(len);
738 if (wavFile == NULL || !wavFile->IsOpen())
739 return TRUE;
741 return WriteFrame(buf, len);
744 BOOL PVXMLIncomingChannel::StartRecording(const PFilePath & fn)
746 // if there is already a file open, close it
747 EndRecording();
749 // open the output file
750 PWaitAndSignal mutex(vxmlMutex);
751 wavFile = CreateWAVFile(fn);
752 PTRACE(1, "Starting recording to " << fn);
753 if (!wavFile->IsOpen()) {
754 PTRACE(2, "OpenIVR\tCannot create record file " << fn);
755 return FALSE;
758 return TRUE;
761 BOOL PVXMLIncomingChannel::EndRecording()
763 PWaitAndSignal mutex(vxmlMutex);
765 if (wavFile == NULL)
766 return TRUE;
768 wavFile->Close();
770 delete wavFile;
771 wavFile = NULL;
773 return TRUE;
776 ///////////////////////////////////////////////////////////////
779 #endif