home *** CD-ROM | disk | FTP | other *** search
/ ftp.muug.mb.ca / 2014.06.ftp.muug.mb.ca.tar / ftp.muug.mb.ca / pub / openh323.tar.gz / openh323.tar / openh323 / src / vpblid.cxx < prev    next >
C/C++ Source or Header  |  2003-08-13  |  17KB  |  688 lines

  1. /*
  2.  * vpblid.cxx
  3.  *
  4.  * Voicetronix VPB4 line interface device
  5.  *
  6.  * Copyright (c) 1999-2000 Equivalence Pty. Ltd.
  7.  *
  8.  * The contents of this file are subject to the Mozilla Public License
  9.  * Version 1.0 (the "License"); you may not use this file except in
  10.  * compliance with the License. You may obtain a copy of the License at
  11.  * http://www.mozilla.org/MPL/
  12.  *
  13.  * Software distributed under the License is distributed on an "AS IS"
  14.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  15.  * the License for the specific language governing rights and limitations
  16.  * under the License.
  17.  *
  18.  * The Original Code is Open H323 Library.
  19.  *
  20.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  21.  *
  22.  * Contributor(s): ______________________________________.
  23.  *
  24.  * Patch: 2002/10/4 Peter Wintulich Peter@voicetronix.com.au
  25.  * IsLineDisconected was looking for any tone to signify hangup/busy.
  26.  * Changed so only BUSY tone reports line hangup/busy.
  27.  *
  28.  * $Log: vpblid.cxx,v $
  29.  * Revision 1.21  2003/08/13 22:02:03  dereksmithies
  30.  * Apply patch from Daniel Bichara to GetOSHandle() for VPB devices. Thanks.
  31.  *
  32.  * Revision 1.20  2003/03/12 00:15:40  dereks
  33.  * Fix compile error on RH8.0
  34.  *
  35.  * Revision 1.19  2003/03/05 06:26:44  robertj
  36.  * Added function to play a WAV file to LID, thanks Pietro Ravasio
  37.  *
  38.  * Revision 1.18  2002/09/03 06:22:26  robertj
  39.  * Cosmetic change to formatting.
  40.  *
  41.  * Revision 1.17  2002/08/01 01:33:42  dereks
  42.  * Adjust verbosity of PTRACE statements.
  43.  *
  44.  * Revision 1.16  2002/07/02 03:20:37  dereks
  45.  * Fix check for line disconnected state.   Remove timer on line ringing.
  46.  *
  47.  * Revision 1.15  2002/07/01 23:57:35  dereks
  48.  * Clear dtmf and tone event queue when changing hook status, to remove spurious events.
  49.  *
  50.  * Revision 1.14  2002/07/01 02:52:52  dereks
  51.  * IsToneDetected now reports the RING tone.   Add PTRACE statements.
  52.  *
  53.  * Revision 1.13  2002/05/21 09:16:31  robertj
  54.  * Fixed segmentation fault, if OpalVPBDevice::StopTone() is called more than
  55.  *   once, thanks Artis Kugevics
  56.  *
  57.  * Revision 1.12  2002/03/20 06:05:04  robertj
  58.  * Improved multithreading support, thanks David Rowe
  59.  *   NOTE: only works with VPB driver version 2.5.5
  60.  *
  61.  * Revision 1.1  2002/03/11 02:42:56  david
  62.  * Initial revision
  63.  *
  64.  * Revision 1.11  2001/11/19 06:35:41  robertj
  65.  * Added tone generation handling
  66.  *
  67.  * Revision 1.10  2001/10/05 03:51:21  robertj
  68.  * Added missing pragma implementation
  69.  *
  70.  * Revision 1.9  2001/10/05 03:33:06  robertj
  71.  * Fixed compatibility with latest VPB drivers
  72.  *
  73.  * Revision 1.8  2001/09/13 05:27:46  robertj
  74.  * Fixed incorrect return type in virtual function, thanks Vjacheslav Andrejev
  75.  *
  76.  * Revision 1.7  2001/05/11 04:43:43  robertj
  77.  * Added variable names for standard PCM-16 media format name.
  78.  *
  79.  * Revision 1.6  2001/01/25 07:27:17  robertj
  80.  * Major changes to add more flexible OpalMediaFormat class to normalise
  81.  *   all information about media types, especially codecs.
  82.  *
  83.  * Revision 1.5  2000/11/24 10:54:45  robertj
  84.  * Modified the ReadFrame/WriteFrame functions to allow for variable length codecs.
  85.  *
  86.  * Revision 1.4  2000/11/20 04:37:03  robertj
  87.  * Changed tone detection API slightly to allow detection of multiple
  88.  * simultaneous tones
  89.  *
  90.  * Revision 1.3  2000/05/02 04:32:28  robertj
  91.  * Fixed copyright notice comment.
  92.  *
  93.  * Revision 1.2  2000/01/07 08:28:09  robertj
  94.  * Additions and changes to line interface device base class.
  95.  *
  96.  * Revision 1.1  1999/12/23 23:02:36  robertj
  97.  * File reorganision for separating RTP from H.323 and creation of LID for VPB support.
  98.  *
  99.  */
  100.  
  101. #include <ptlib.h>
  102.  
  103. #ifdef __GNUC__
  104. #pragma implementation "vpblid.h"
  105. #endif
  106.  
  107. #include "vpblid.h"
  108.  
  109. #include <vpbapi.h>
  110.  
  111.  
  112. #define new PNEW
  113.  
  114.  
  115. /////////////////////////////////////////////////////////////////////////////
  116.  
  117. OpalVpbDevice::OpalVpbDevice()
  118. {
  119.   cardNumber = 0;
  120.   lineCount = 0;
  121.   vpb_seterrormode(VPB_ERROR_CODE);
  122. }
  123.  
  124.  
  125. BOOL OpalVpbDevice::Open(const PString & device)
  126. {
  127.   Close();
  128.  
  129.   cardNumber = device.AsUnsigned(10);
  130.  
  131.   lineCount = 0;
  132.   while (lineCount < MaxLineCount && lineState[lineCount].Open(cardNumber, lineCount))
  133.     lineCount++;
  134.  
  135.   os_handle = lineCount > 0 ? 1 : -1;
  136.  
  137.   return IsOpen();
  138. }
  139.  
  140.  
  141. BOOL OpalVpbDevice::LineState::Open(unsigned cardNumber, unsigned lineNumber)
  142. {
  143.   handle = vpb_open(cardNumber, lineNumber+1);
  144.   if (handle < 0)
  145.     return FALSE;
  146.  
  147.   readIdle = writeIdle = TRUE;
  148.   readFrameSize = writeFrameSize = 480;
  149.   currentHookState = FALSE;
  150.   vpb_sethook_sync(handle, VPB_ONHOOK);
  151.   vpb_set_event_mask(handle, VPB_MRING | VPB_MTONEDETECT );
  152.   myToneThread = NULL;
  153.  
  154.   return TRUE;
  155. }
  156.  
  157.  
  158. BOOL OpalVpbDevice::Close()
  159. {
  160.   for (unsigned line = 0; line < lineCount; line++)
  161.     vpb_close(lineState[line].handle);
  162.  
  163.   os_handle = -1;
  164.   return TRUE;
  165. }
  166.  
  167.  
  168. PString OpalVpbDevice::GetName() const
  169. {
  170.   char buf[100];
  171.   vpb_get_model(buf);
  172.   return psprintf("%s/%u", buf, cardNumber);
  173. }
  174.  
  175.  
  176. unsigned OpalVpbDevice::GetLineCount()
  177. {
  178.   return lineCount;
  179. }
  180.  
  181. BOOL OpalVpbDevice::IsLineDisconnected(unsigned line, BOOL /*checkForWink*/)
  182. {
  183.   //  unsigned thisTone = IsToneDetected(line);
  184.   BOOL lineIsDisconnected = (IsToneDetected(line) == BusyTone);
  185.  
  186.   PTRACE(3, "VPB\tLine " << line << " is disconnected: " << (lineIsDisconnected ? " TRUE" : "FALSE"));
  187.   return lineIsDisconnected;
  188. }
  189.  
  190. BOOL OpalVpbDevice::IsLineOffHook(unsigned line)
  191. {
  192.   if (line >= MaxLineCount)
  193.     return FALSE;
  194.  
  195.   return lineState[line].currentHookState;
  196. }
  197.  
  198.  
  199. BOOL OpalVpbDevice::SetLineOffHook(unsigned line, BOOL newState)
  200. {
  201.   if (line >= MaxLineCount)
  202.     return FALSE;
  203.  
  204.   return lineState[line].SetLineOffHook(newState);
  205. }
  206.  
  207.  
  208. BOOL OpalVpbDevice::LineState::SetLineOffHook(BOOL newState)
  209. {
  210.   currentHookState = newState;
  211.   VPB_EVENT        event;
  212.  
  213.   BOOL setHookOK = vpb_sethook_sync(handle, newState ? VPB_OFFHOOK : VPB_ONHOOK) >= 0;
  214.   PTRACE(3, "vpb\tSetLineOffHook to " << (newState ? "offhook" : "on hook") << 
  215.      (setHookOK ? " succeeded." : " failed."));
  216.  
  217.   // clear DTMF buffer and event queue after changing hook state.
  218.   vpb_flush_digits(handle);   
  219.   while (vpb_get_event_ch_async(handle, &event) == VPB_OK);
  220.  
  221.   return setHookOK;
  222. }
  223.  
  224.  
  225. BOOL OpalVpbDevice::IsLineRinging(unsigned line, DWORD * cadence)
  226. {
  227.   if (line >= MaxLineCount)
  228.     return FALSE;
  229.  
  230.   return lineState[line].IsLineRinging(cadence);
  231. }
  232.  
  233.  
  234. BOOL OpalVpbDevice::LineState::IsLineRinging(DWORD * /*cadence*/)
  235. {
  236.   VPB_EVENT event;
  237.   BOOL lineIsRinging = FALSE;
  238.  
  239.   if (currentHookState) {
  240.     PTRACE(6, "VPB\tTest IsLineRinging() returns FALSE");
  241.     return FALSE;
  242.   }
  243.  
  244.   // DR 13/1/02 - Dont look at event queue here if off hook, as we will steal events 
  245.   // that IsToneDetected may be looking for.
  246.   
  247.   if (vpb_get_event_ch_async(handle, &event) == VPB_OK) 
  248.     if (event.type == VPB_RING) {
  249.       PTRACE(3, "VPB\tRing event detected in IsLineRinging");
  250.       lineIsRinging = TRUE;
  251.     }
  252.  
  253.   return lineIsRinging;
  254. }
  255.  
  256.  
  257.   
  258.  
  259. static const struct {
  260.   const char * mediaFormat;
  261.   WORD         mode;
  262. } CodecInfo[] = {
  263.   { OPAL_PCM16,       VPB_LINEAR },
  264.   { "G.711-uLaw-64k", VPB_MULAW },
  265.   { "G.711-ALaw-64k", VPB_ALAW  },
  266. };
  267.  
  268.  
  269. OpalMediaFormat::List OpalVpbDevice::GetMediaFormats() const
  270. {
  271.   OpalMediaFormat::List formats;
  272.  
  273.   for (PINDEX i = 0; i < PARRAYSIZE(CodecInfo); i++)
  274.     formats.Append(new OpalMediaFormat(CodecInfo[i].mediaFormat));
  275.  
  276.   return formats;
  277. }
  278.  
  279.  
  280. static PINDEX FindCodec(const OpalMediaFormat & mediaFormat)
  281. {
  282.   for (PINDEX codecType = 0; codecType < PARRAYSIZE(CodecInfo); codecType++) {
  283.     if (mediaFormat == CodecInfo[codecType].mediaFormat)
  284.       return codecType;
  285.   }
  286.  
  287.   return P_MAX_INDEX;
  288. }
  289.  
  290.  
  291. BOOL OpalVpbDevice::SetReadFormat(unsigned line, const OpalMediaFormat & mediaFormat)
  292. {
  293.   if (line >= MaxLineCount)
  294.     return FALSE;
  295.  
  296.   PTRACE(4, "VPB\tSetReadFormat(" << mediaFormat << ')');
  297.  
  298.   lineState[line].readFormat = FindCodec(mediaFormat);
  299.   if (lineState[line].readFormat == P_MAX_INDEX)
  300.     return FALSE;
  301.  
  302.   if (vpb_record_buf_start(lineState[line].handle,
  303.                            CodecInfo[lineState[line].readFormat].mode) < 0)
  304.     return FALSE;
  305.  
  306.   lineState[line].readIdle = FALSE;
  307.   return TRUE;
  308. }
  309.  
  310.  
  311. BOOL OpalVpbDevice::SetWriteFormat(unsigned line, const OpalMediaFormat & mediaFormat)
  312. {
  313.   if (line >= MaxLineCount)
  314.     return FALSE;
  315.  
  316.   PTRACE(4, "VPB\tSetWriteFormat(" << mediaFormat << ')');
  317.  
  318.   lineState[line].writeFormat = FindCodec(mediaFormat);
  319.   if (lineState[line].writeFormat == P_MAX_INDEX)
  320.     return FALSE;
  321.   lineState[line].DTMFplaying = FALSE;
  322.  
  323.   if (vpb_play_buf_start(lineState[line].handle,
  324.                          CodecInfo[lineState[line].writeFormat].mode) < 0)
  325.     return FALSE;
  326.  
  327.   lineState[line].writeIdle = FALSE;
  328.   return TRUE;
  329. }
  330.  
  331. OpalMediaFormat OpalVpbDevice::GetReadFormat(unsigned line)
  332. {
  333.   if (lineState[line].readFormat == P_MAX_INDEX)
  334.     return "";
  335.   return CodecInfo[lineState[line].readFormat].mediaFormat;
  336. }
  337.  
  338.  
  339. OpalMediaFormat OpalVpbDevice::GetWriteFormat(unsigned line)
  340. {
  341.   if (lineState[line].writeFormat == P_MAX_INDEX)
  342.     return "";
  343.   return CodecInfo[lineState[line].writeFormat].mediaFormat;
  344. }
  345.  
  346.  
  347. BOOL OpalVpbDevice::StopReadCodec(unsigned line)
  348. {
  349.   if (line >= MaxLineCount)
  350.     return FALSE;
  351.  
  352.   PTRACE(3, "VPB\tStopReadCodec");
  353.  
  354.   if (lineState[line].readIdle)
  355.     return FALSE;
  356.  
  357.   PTRACE(3, "VPB\tStopReadCodec before");
  358.   vpb_record_terminate(lineState[line].handle);
  359.   vpb_record_buf_finish(lineState[line].handle);
  360.   PTRACE(3, "VPB\tStopReadCodec after");
  361.  
  362.   lineState[line].readIdle = TRUE;
  363.   return TRUE;
  364. }
  365.  
  366.  
  367. BOOL OpalVpbDevice::StopWriteCodec(unsigned line)
  368. {
  369.   if (line >= MaxLineCount)
  370.     return FALSE;
  371.  
  372.   PTRACE(1, "VPB\tStopWriteCodec");
  373.  
  374.   if (lineState[line].writeIdle)
  375.     return FALSE;
  376.  
  377.   PTRACE(3, "VPB\tStopWriteCodec before");
  378.   vpb_play_terminate(lineState[line].handle);
  379.   vpb_play_buf_finish(lineState[line].handle);
  380.   PTRACE(3, "VPB\tStopWriteCodec after");
  381.  
  382.   lineState[line].writeIdle = TRUE;
  383.   return TRUE;
  384. }
  385.  
  386.  
  387. BOOL OpalVpbDevice::SetReadFrameSize(unsigned line, PINDEX size)
  388. {
  389.   if (line >= MaxLineCount)
  390.     return FALSE;
  391.  
  392.   lineState[line].readFrameSize = size;
  393.   return TRUE;
  394. }
  395.  
  396.  
  397. BOOL OpalVpbDevice::SetWriteFrameSize(unsigned line, PINDEX size)
  398. {
  399.   if (line >= MaxLineCount)
  400.     return FALSE;
  401.  
  402.   lineState[line].writeFrameSize = size;
  403.   return TRUE;
  404. }
  405.  
  406.  
  407. PINDEX OpalVpbDevice::GetReadFrameSize(unsigned line)
  408. {
  409.   if (line >= MaxLineCount)
  410.     return FALSE;
  411.  
  412.   return lineState[line].readFrameSize;
  413. }
  414.  
  415.  
  416. PINDEX OpalVpbDevice::GetWriteFrameSize(unsigned line)
  417. {
  418.   if (line >= MaxLineCount)
  419.     return FALSE;
  420.  
  421.   return lineState[line].writeFrameSize;
  422. }
  423.  
  424.  
  425. BOOL OpalVpbDevice::ReadFrame(unsigned line, void * buf, PINDEX & count)
  426. {
  427.   if (line >= MaxLineCount)
  428.     return FALSE;
  429.  
  430.   count = lineState[line].readFrameSize;
  431.   PTRACE(4, "VPB\tReadFrame before vpb_record_buf_sync");
  432.   vpb_record_buf_sync(lineState[line].handle, (char *)buf, (WORD)count);
  433.   PTRACE(4, "VPB\tReadFrame after vpb_record_buf_sync");
  434.   return TRUE;
  435. }
  436.  
  437.  
  438. BOOL OpalVpbDevice::WriteFrame(unsigned line, const void * buf, PINDEX count, PINDEX & written)
  439. {
  440.   written = 0;
  441.   if (line >= MaxLineCount)
  442.     return FALSE;
  443.  
  444.   PTRACE(4, "VPB\tWriteFrame before vpb_play_buf_sync");
  445.   vpb_play_buf_sync(lineState[line].handle, (char *)buf,(WORD)count);
  446.   PTRACE(4, "VPB\tWriteFrame after vpb_play_buf_sync");
  447.  
  448.   written = count;
  449.   return TRUE;
  450. }
  451.  
  452.  
  453. BOOL OpalVpbDevice::SetRecordVolume(unsigned line, unsigned volume)
  454. {
  455.   if (line >= MaxLineCount)
  456.     return FALSE;
  457.  
  458.   return vpb_record_set_gain(lineState[line].handle, (float)(volume/100.0*24.0-12.0)) >= 0;
  459. }
  460.  
  461. BOOL OpalVpbDevice::SetPlayVolume(unsigned line, unsigned volume)
  462. {
  463.   if (line >= MaxLineCount)
  464.     return FALSE;
  465.  
  466.   return vpb_play_set_gain(lineState[line].handle, (float)(volume/100.0*24.0-12.0)) >= 0;
  467. }
  468.  
  469.  
  470. char OpalVpbDevice::ReadDTMF(unsigned line)
  471. {
  472.   if (line >= MaxLineCount)
  473.     return '\0';
  474.  
  475.   VPB_DIGITS vd;
  476.   vd.term_digits = "";
  477.   vd.max_digits = 1;
  478.   vd.digit_time_out = 10;
  479.   vd.inter_digit_time_out = 10;
  480.  
  481.   char buf[VPB_MAX_STR];
  482.  
  483.   if (vpb_get_digits_sync(lineState[line].handle, &vd, buf) == VPB_DIGIT_MAX) {
  484.     PTRACE(3, "VPB\tReadDTMF (digit)" << buf[0]);
  485.     return buf[0];
  486.   }
  487.  
  488.   return '\0';
  489. }
  490.  
  491. /*
  492. BOOL OpalVpbDevice::PlayFile(unsigned line, const PString & fn, BOOL syncOff=FALSE)
  493. {
  494.     PTRACE(3, "VPB\tSono entrato in PlayFile per leggere il file " << fn << " sulla linea " <<line);
  495.     
  496.     char * filename;
  497.     strcpy(filename,fn);
  498.         
  499.     if(syncOff)
  500.     {
  501.         vpb_play_file_async(lineState[line].handle, filename, VPB_PLAYEND);
  502.     }
  503.     else
  504.     {
  505.         vpb_play_file_sync(lineState[line].handle, filename);
  506.     }
  507.     return TRUE;
  508. }
  509. */
  510.  
  511. /*
  512. // Ritorna il codice dell'evento sull'handle lineState[line].handle
  513. int OpalVpbDevice::GetVPBEvent(unsigned line)
  514. {
  515.     return vpb_get_event_mask(lineState[line].handle;
  516. }
  517. */
  518.  
  519. BOOL OpalVpbDevice::PlayDTMF(unsigned line, const char * digits, DWORD, DWORD)
  520. {
  521.   if (line >= MaxLineCount)
  522.     return FALSE;
  523.  
  524.   PTRACE(3, "VPB\tPlayDTMF: " << digits);
  525.   vpb_dial_sync(lineState[line].handle, (char *)digits);
  526.   vpb_dial_sync(lineState[line].handle, ",");
  527.  
  528.   return TRUE;
  529. }
  530.  
  531.  
  532. int OpalVpbDevice::GetOSHandle(unsigned line)
  533. {
  534.   return lineState[line].handle;
  535. }
  536.  
  537. unsigned OpalVpbDevice::IsToneDetected(unsigned line)
  538. {
  539.   if (line >= MaxLineCount) {
  540.     PTRACE(3, "VPB\tTone Detect no tone detected, line is > MaxLineCount (" << MaxLineCount << ")");
  541.     return NoTone;
  542.   }
  543.  
  544.   VPB_EVENT event;
  545.   if (vpb_get_event_ch_async(lineState[line].handle, &event) == VPB_NO_EVENTS) {
  546.     PTRACE(3, "VPB\tTone Detect no events on line " << line << " in  tone detected");    
  547.     return NoTone;
  548.   }
  549.  
  550.   if (event.type == VPB_RING) {
  551.     PTRACE(3, "VPB\t Tone Detect: Ring tone (generated from ring event)");
  552.     return RingTone;
  553.   }
  554.  
  555.   if (event.type != VPB_TONEDETECT) {
  556.     PTRACE(3, "VPB\tTone Detect. Event type is not (ring | tone). No tone detected.");
  557.     return NoTone;
  558.   }
  559.  
  560.   switch (event.data) {
  561.     case VPB_DIAL :
  562.       PTRACE(3, "VPB\tTone Detect: Dial tone.");
  563.       return DialTone;
  564.  
  565.     case VPB_RINGBACK :
  566.       PTRACE(3, "VPB\tTone Detect: Ring tone.");
  567.       return RingTone;
  568.  
  569.     case VPB_BUSY :
  570.       PTRACE(3, "VPB\tTone Detect: Busy tone.");
  571.       return BusyTone;
  572.  
  573.     case VPB_GRUNT :
  574.       PTRACE(3, "VPB\tTone Detect: Grunt tone.");
  575.       break;
  576.   }
  577.  
  578.   return NoTone;
  579. }
  580.  
  581. BOOL OpalVpbDevice::PlayTone(unsigned line, CallProgressTones tone)
  582. {
  583.   VPB_TONE vpbtone;    
  584.     
  585.   PTRACE(3, "VPB\tPlayTone STARTED");
  586.  
  587.   switch(tone) {
  588.   case DialTone:
  589.     PTRACE(3, "VPB\tPlayTone DialTone");
  590.     vpbtone.freq1 = 425;
  591.     vpbtone.freq2 = 450;
  592.     vpbtone.freq3 = 400;
  593.     vpbtone.level1 = -12;
  594.     vpbtone.level2 = -18;
  595.     vpbtone.level3 = -18;
  596.     vpbtone.ton = 30000;
  597.     vpbtone.toff = 10;
  598.     lineState[line].myToneThread = new ToneThread(
  599.                           lineState[line].handle,
  600.                           vpbtone
  601.                           );
  602.     break;
  603.  
  604.   case BusyTone:
  605.     vpbtone.freq1 = 425;
  606.     vpbtone.freq2 = 0;
  607.     vpbtone.freq3 = 0;
  608.     vpbtone.level1 = -12;
  609.     vpbtone.level2 = -100;
  610.     vpbtone.level3 = -100;
  611.     vpbtone.ton = 325;
  612.     vpbtone.toff = 750;
  613.     lineState[line].myToneThread = new ToneThread(
  614.                           lineState[line].handle,
  615.                           vpbtone
  616.                           );
  617.     break;
  618.   default:
  619.     return FALSE;
  620.   }
  621.   
  622.   return TRUE;
  623. }
  624.  
  625. BOOL OpalVpbDevice::StopTone(unsigned line)
  626. {
  627.   PTRACE(3, "VPB\tStopTone STARTED");
  628.   if (lineState[line].myToneThread) {
  629.     delete lineState[line].myToneThread;
  630.     lineState[line].myToneThread = NULL;
  631.   }
  632.   PTRACE(3, "VPB\tStopTone FINSISHED");
  633.   return TRUE;
  634. }
  635.  
  636. BOOL OpalVpbDevice::PlayAudio(unsigned line, const PString & fn)
  637. {
  638.   PTRACE(3, "VPB\tPlayAudio starting a new Audio Thread on line " << line << " with file " << fn);
  639.   vpb_play_file_async(lineState[line].handle, (char *)&fn, VPB_PLAYEND);
  640.   /*
  641.   lineState[line].myAudioThread = new AudioThread(
  642.   */
  643.   return TRUE;
  644. }
  645.  
  646. BOOL OpalVpbDevice::StopAudio(unsigned line)
  647. {
  648.   PTRACE(3, "VPB\tStopAudio STARTED");
  649.   /*
  650.   if (lineState[line].myAudioThread) {
  651.     delete lineState[line].myAudioThread;
  652.     lineState[line].myAudioThread = NULL;
  653.   }
  654.   */
  655.   vpb_play_terminate(lineState[line].handle);
  656.   PTRACE(3, "VPB\tStopAudio FINISHED");
  657.   return TRUE;
  658. }
  659.  
  660.  
  661. /////////////////////////////////////////////////////////////////////////////
  662.  
  663. ToneThread::ToneThread(int ahandle, VPB_TONE avpbtone) : PThread(10000, NoAutoDeleteThread) {
  664.   handle = ahandle;
  665.   vpbtone = avpbtone;
  666.   Resume();
  667. }
  668.  
  669. ToneThread::~ToneThread() {
  670.   PTRACE(3, "VPB\tToneThread Destructor STARTED");
  671.   vpb_tone_terminate(handle);
  672.   shutdown.Signal();
  673.   WaitForTermination();
  674.   PTRACE(3, "VPB\tToneThread Destructor FINISHED");
  675. }
  676.  
  677. void ToneThread::Main() {
  678.   PTRACE(3, "VPB\tToneThread Main STARTED");
  679.   while (!shutdown.Wait(10)) {
  680.     vpb_playtone_sync(handle, &vpbtone);
  681.     PTRACE(3, "VPB\tvpl_playtone_sync returned");
  682.   }
  683.   PTRACE(3, "VPB\tToneThread Main FINISHED");
  684. }
  685.  
  686.  
  687. /////////////////////////////////////////////////////////////////////////////
  688.