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
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
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
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
61 #include <lpc10codec.h>
64 #include <ptclib/delaychan.h>
65 #include <ptclib/pwavfile.h>
66 #include <ptclib/memfile.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())
89 else if (mediaFormat
== OPAL_G7231_6k3
)
92 PTRACE(2, "OpenIVR\tUnknown media format " << mediaFormat
);
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())
109 else if (mediaFormat
== OPAL_G7231_6k3
)
112 PTRACE(2, "OpenIVR\tUnknown media format " << mediaFormat
);
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
);
152 BOOL
OpalVXMLSession::PlayData(const PBYTEArray
& data
)
154 if (outgoingChannel
!= NULL
)
155 outgoingChannel
->QueueData(data
);
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
);
173 BOOL
OpalVXMLSession::EndRecording()
175 if (incomingChannel
!= NULL
)
176 return incomingChannel
->EndRecording();
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");
196 // Check the wave file format
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() );
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");
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() );
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
)
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
)) {
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
)
317 BOOL result
= PIndirectChannel::Read(frameBuffer
.GetPointer(), frameLen
);
319 // if we did not read a full frame of audio, fill the end of the
321 PINDEX count
= GetLastReadCount();
322 if (count
< frameLen
)
323 memset(frameBuffer
.GetPointer()+count
, 0, frameLen
-count
);
328 void OpalVXMLOutgoingChannelPCM::CreateSilenceFrame(PINDEX amount
)
332 memset(frameBuffer
.GetPointer(), 0, frameLen
);
335 ///////////////////////////////////////////////////////////////
337 OpalVXMLOutgoingChannelG7231::OpalVXMLOutgoingChannelG7231(OpalVXMLSession
& vxml
)
338 : OpalVXMLOutgoingChannel(vxml
)
342 void OpalVXMLOutgoingChannelG7231::QueueFile(const PString
& 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")
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*/)
371 BOOL
OpalVXMLOutgoingChannelG7231::ReadFrame(PINDEX
/*amount*/)
373 if (!PIndirectChannel::Read(frameBuffer
.GetPointer(), 1))
377 frameLen
= G7231_File_Codec::GetFrameLen(frameBuffer
[0]);
379 return PIndirectChannel::Read(frameBuffer
.GetPointer()+1, frameLen
-1);
382 void OpalVXMLOutgoingChannelG7231::CreateSilenceFrame(PINDEX
/*amount*/)
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());
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
;
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
;
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
)
452 if (!rawDataChannel
->Read(buffer
, 1)) {
453 cerr
<< "Read failed" << endl
;
456 if (rawDataChannel
->GetLastReadCount() == 0) {
457 cerr
<< "Read count is zero" << endl
;
461 int readLen
= G7231_File_Codec::GetFrameLen(buffer
[0]);
464 if (!rawDataChannel
->Read(buffer
+1, readLen
-1)) {
465 cerr
<< "Read failed" << endl
;
468 if (rawDataChannel
->GetLastReadCount() == 0) {
469 cerr
<< "Read count is zero" << endl
;
478 BOOL
G7231_File_Codec::Write(const BYTE
* buffer
, unsigned length
, const RTP_DataFrame
& /* rtp */, unsigned & writtenLength
)
480 if (rawDataChannel
== NULL
)
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,
487 // If the length is zero, output silence to the file..
489 PTRACE(1,"G.723.1 zero length frame");
491 return rawDataChannel
->Write(silence
, 24);
495 switch (buffer
[0]&3) {
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");
507 return rawDataChannel
->Write(silence
, 24);
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
)
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
;
543 //PAssert((frameOffs + amount) <= frameLen, "Reading past end of frame");
545 memcpy(buffer
, frameBuffer
.GetPointer()+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() << "\"");
576 PTRACE(1, "Playing file \"" << chan
->GetName() << "\"");
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
)
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
);
613 // if we are returning silence frames, then decrement the frame count
617 // if a channel is already open, don't do silence
618 else if (GetBaseReadChannel() != NULL
)
621 // If not in silence and no existing channel, open a new file.
623 PVXMLQueueItem
* qItem
= playQueue
.Dequeue();
634 // if not doing silence, try and read from the file
637 if (ReadFrame(amount
)) {
638 frameBoundary
= AdjustFrame(buffer
, amount
);
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)
652 silentCount
= 5; // always do 5 frames of silence after every file
660 // start silence frame if required
662 CreateSilenceFrame(amount
);
663 frameBoundary
= AdjustFrame(buffer
, amount
);
666 // delay to synchronise to frame boundary
673 BOOL
PVXMLOutgoingChannel::Close()
675 PWaitAndSignal
mutex(vxmlMutex
);
676 PIndirectChannel::Close();
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
);
688 PTRACE(1, "Cannot create WAV file");
690 else if (!chan
->IsOpen()) {
691 PTRACE(1, "Cannot open file \"" << chan
->GetName() << '"');
694 PTRACE(1, "Playing file \"" << chan
->GetName() << "\"");
695 outgoingChannel
.SetReadChannel(chan
, TRUE
);
697 } else { // raw file (eg .sw)
699 chan
= new PFile(fn
);
700 if (!chan
->Open(PFile::ReadOnly
)) {
701 PTRACE(1, "Cannot open file \"" << chan
->GetName() << "\"");
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
)
727 PVXMLIncomingChannel::~PVXMLIncomingChannel()
732 BOOL
PVXMLIncomingChannel::Write(const void * buf
, PINDEX len
)
734 PWaitAndSignal
mutex(vxmlMutex
);
738 if (wavFile
== NULL
|| !wavFile
->IsOpen())
741 return WriteFrame(buf
, len
);
744 BOOL
PVXMLIncomingChannel::StartRecording(const PFilePath
& fn
)
746 // if there is already a file open, close it
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
);
761 BOOL
PVXMLIncomingChannel::EndRecording()
763 PWaitAndSignal
mutex(vxmlMutex
);
776 ///////////////////////////////////////////////////////////////