/*
 * cmds.cxx
 *
 * Auxiliary commands for OpenAM
 *
 * Copyright (c) 1993-2001 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Contributor(s): ______________________________________.
 *
 * $Log: cmds.cxx,v $
 * Revision 1.9  2004/01/31 07:38:21  rjongbloed
 * Changed HAS_IXJ usage to if defined not if non-zero
 *
 * Revision 1.8  2002/02/03 22:57:22  rogerh
 * Add support for playback and recording on the PSTN line using --port 2
 *
 * Revision 1.7  2002/01/26 00:18:19  robertj
 * Fixed MSVC warnings.
 * Removed unix specific symbol PORT_POTS
 *
 * Revision 1.6  2002/01/25 14:48:34  rogerh
 * Add a console thread which lets us terminate recording or playback
 * when the Enter key is pressed
 *
 * Revision 1.5  2002/01/25 11:50:10  rogerh
 * dd --port option to play and record commands so you can use a
 * telephone on the POTS port or use speakers and microphone.
 *
 * Revision 1.4  2002/01/13 20:55:59  rogerh
 * Update to allow proper recording and playback of both PCM and G.723.1 .WAV
 * files as well as .sw and .g7231 files
 *
 * Revision 1.3  2002/01/11 14:34:20  rogerh
 * Fix play command so it plays G.723.1 files correctly
 *
 * Revision 1.2  2001/09/25 01:10:30  robertj
 * Fixed compile problerm. Still incomplete code.
 *
 * Revision 1.1  2001/09/24 22:39:42  craigs
 * Added commands to play and record data files, esp G.723.1
 *
 */

#include <ptlib.h>
#include <ixjlid.h>

#include "version.h"
#include "main.h"

class ConsoleThread : public PThread {
  PCLASSINFO(ConsoleThread, PThread);
  public:
    ConsoleThread(PMutex *_enter, BOOL *_enter_pressed) : PThread(1000,AutoDeleteThread)
    {
      enter = _enter;
      enter_pressed = _enter_pressed;
      
      Resume(); // start running this thread as soon as the thread is created.
    }

    void Main() {
      PConsoleChannel console(PConsoleChannel::StandardInput);

      // wait for Enter to be pressed
      console.peek();

      enter->Wait();
      *enter_pressed = TRUE;
      enter->Signal();
    }
  private:
    PMutex *enter;
    BOOL   *enter_pressed;
};

#define new PNEW

#ifdef HAS_IXJ

#define	G7231_BUFFER_SIZE	24
#define	PCM_BUFFER_SIZE		480

extern PString G7231Ext;
extern PString WAVExt;
extern PString PCMExt;

extern BOOL CheckWAVFileValid(PWAVFile *chan, int type);

#define CHECK_PCM   1
#define CHECK_G7231 2





static BOOL DetermineType(PArgList & args, const PFilePath & fn, BOOL & isPCM, BOOL & isWAV, BOOL openExisting)
{
  BOOL knowType = FALSE;

  // Handle .WAV file types
  if ((fn.GetType() *= WAVExt) && (openExisting)) {

    // determine the file type by peeking into the existing file

    PWAVFile *wavfile = new PWAVFile(fn, PFile::ReadOnly);
    if (!wavfile->IsOpen()) {
      PError << "error: cannot open file \"" << fn << "\"" << endl;
      delete wavfile;
      return FALSE;
    }
    if (CheckWAVFileValid(wavfile,CHECK_G7231))    isPCM = FALSE;
    else if (CheckWAVFileValid(wavfile,CHECK_PCM)) isPCM = TRUE;
    else {
      PError << "error: invalid wav file format for file \"" << fn << "\"" << endl;
      delete wavfile;
      return FALSE;
    }
    wavfile->Close();
    delete wavfile;

    isWAV    = TRUE;
    knowType = TRUE;
  }

  if ((fn.GetType() *= WAVExt) && (!openExisting)) {
    // determine the file type from the command line options
    if (args.HasOption("pcm")) {
      isPCM    = TRUE;
      isWAV    = TRUE;
      knowType = TRUE;
    } else if (args.HasOption("g7231")) {
      isPCM    = FALSE;
      isWAV    = TRUE;
      knowType = TRUE;
    } else {
      PError << "usage: recording a .wav file requires either --g7231 or --pcm,"
             << endl;
      return FALSE;
    }
  }

  // For non .WAV files determine file type from filename extension
  if (fn.GetType() *= G7231Ext) {
    isPCM    = FALSE;
    isWAV    = FALSE;
    knowType = TRUE;
  }

  if (fn.GetType() *= PCMExt) {
    isPCM    = TRUE;
    isWAV    = FALSE;
    knowType = TRUE;
  }

  // if we still do not know the file type, use the command line options
  if (!knowType) {
    if (args.HasOption("pcm")) {
      isPCM    = TRUE;
      isWAV    = FALSE;
      knowType = TRUE;
    } else if (args.HasOption("g7231")) {
      isPCM    = FALSE;
      isWAV    = FALSE;
      knowType = TRUE;
    }
  }

  // error if we could not determine the file type
  if (!knowType) {
    PError << "usage: command requires either --g7231 or --pcm,"
           << "       or filename with " << WAVExt << " or " << G7231Ext
           << " or " << PCMExt << " extension" << endl;
    return FALSE;
  }

  return TRUE;
}

static BOOL OpenDevice(OpalIxJDevice & xJack, const PString & ixjDevice, int port)
{
  if (!xJack.Open(ixjDevice)) {
    PError << "error: cannot open device \"" << ixjDevice << "\"" << endl;
    return FALSE;
  }

  xJack.SetLineToLineDirect(0, 1, FALSE);

  if (port == 3) xJack.EnableAudio(0, FALSE);
  if (port == 2) xJack.EnableAudio(OpalIxJDevice::PSTNLine, TRUE);
  if (port == 1) xJack.EnableAudio(OpalIxJDevice::POTSLine, TRUE);

  return TRUE;
}

void OpenAm::RecordFile(PArgList & args)
{
  if (args.GetCount() < 2) {
    PError << "usage: openam record [--port n] [--pcm|--g7231] -q N  fn" << endl;
    PError << "usage: port 1=POTS 3=MIC+SPEAKER" << endl;
    PError << "usage: -q N   quicknet device. Eg -q 0" << endl;
    PError << "usage: -fn    filename" << endl;
    return;
  }

  PFilePath fn  = args[1];
  BOOL isPCM = FALSE;
  BOOL isWAV = FALSE;
  int port;

  // Determine the type of file
  if (!DetermineType(args, fn, isPCM, isWAV, FALSE)) return;

  PFile *recordFile;
  if (isWAV) {
    if (isPCM) recordFile = new PWAVFile(PWAVFile::PCM_WavFile);
    else       recordFile = new PWAVFile(PWAVFile::G7231_WavFile);
  }
  else       recordFile = new PFile();

  if (recordFile == NULL)
    return;

  // open the file
  if (!recordFile->Open(fn, PFile::WriteOnly)) {
    PError << "error: cannot open file \"" << fn << "\"" << endl;
    return;
  }

  // open the device
  if (!args.HasOption('q')) {
    PError << "error: record command requires -q option for Quicknet device" << endl;
    return;
  }

  cout << "ok" << endl
       << "Recording " << (isPCM ? "PCM" : "G.723.1")
       << (isWAV ? " wav message" : " message") << endl;

  if (args.HasOption("port")) {
    port = args.GetOptionString("port").AsInteger();
  } else {
    port = OpalIxJDevice::POTSLine; // default to a real telephone
  }

  OpalIxJDevice xJack;
  if (!OpenDevice(xJack, args.GetOptionString('q'),port)) 
    return;

  // ring handset
  if (port == OpalIxJDevice::POTSLine) {
    xJack.RingLine(OpalIxJDevice::POTSLine, 0x33);

    // wait for answer
    cout << "Waiting for phone to go offhook...";
    cout.flush();

    while (!xJack.IsLineOffHook(OpalIxJDevice::POTSLine))
      Sleep(100);

    cout << endl;
  }

  // start codecs
  if (!xJack.SetReadCodec (OpalIxJDevice::POTSLine,
                           isPCM ? RTP_DataFrame::L16_Mono : RTP_DataFrame::G7231)) {
    PError << "error: error during SetReadCodec" << endl;
    return;
  }
  if (!xJack.SetWriteCodec(OpalIxJDevice::POTSLine,
                           isPCM ? RTP_DataFrame::L16_Mono : RTP_DataFrame::G7231)) {
    PError << "error: error during SetWriteCodec" << endl;
    return;
  }

  // determine the read buffer size
  PINDEX bufferSize;
  if (isPCM)
    bufferSize = xJack.GetReadFrameSize(OpalIxJDevice::POTSLine);
  else
    bufferSize = G7231_BUFFER_SIZE;

  // allocate a buffer
  PBYTEArray buffer;
  buffer.SetSize(bufferSize);

  // Create the console thread.
  PMutex enter;
  BOOL enter_pressed = FALSE;
  PThread * consolethread;
  consolethread = new ConsoleThread( &enter, &enter_pressed);

  cout << "Recording Started." << endl << "To stop recording " ;
  if (port == OpalIxJDevice::POTSLine)
    cout << "put the phone back onhook or ";
  cout << "press Enter" << endl;

  // start recording
  for (;;) {

    // stop if we are using POTS and the telephone handset it put down
    if ((port == OpalIxJDevice::POTSLine) && (!xJack.IsLineOffHook(OpalIxJDevice::POTSLine)))
      break;

    // stop if the user pressed Enter on the console
    enter.Wait();
    if (enter_pressed == TRUE) {
      enter.Signal();
      break;
    }
    enter.Signal();

    PINDEX count;
    if (!xJack.ReadFrame(OpalIxJDevice::POTSLine, buffer.GetPointer(), count)) {
      PError << "error: error during ReadFrame" << endl;
      return;
    }
    recordFile->Write(buffer, count);
  }

  cout << "Recording Finished" << endl;

  // stop recording;
  xJack.StopReadCodec (OpalIxJDevice::POTSLine);
  xJack.StopWriteCodec(OpalIxJDevice::POTSLine);

  // close the file
  recordFile->Close();
  delete recordFile;
}


void OpenAm::PlayFile(PArgList & args)
{
  if (args.GetCount() < 2) {
    PError << "usage: openam play [--port n] [--pcm|--g7231] -q N  fn" << endl;
    PError << "usage: port 1=POTS 2=PSTN 3=MIC+SPEAKER" << endl;
    PError << "usage: -q N   quicknet device. Eg -q 0" << endl;
    PError << "usage: -fn    filename" << endl;
    return;
  }

  PFilePath fn  = args[1];
  BOOL isPCM = FALSE;
  BOOL isWAV = FALSE;
  int port;

  // Determine the type of file
  if (!DetermineType(args, fn, isPCM, isWAV, TRUE)) return;

  PFile *playFile;
  if (isWAV) playFile = new PWAVFile();
  else       playFile = new PFile();

  if (playFile == NULL)
    return;

  // open the file
  if (!playFile->Open(fn, PFile::ReadOnly)) {
    PError << "error: cannot open file \"" << fn << "\"" << endl;
    return;
  }

  // open the device
  if (!args.HasOption('q')) {
    PError << "error: play command requires -q option for Quicknet device" << endl;
    return;
  }

  cout << "Playing " << (isPCM ? "PCM" : "G.723.1")
       << (isWAV ? " wav message" : " message") << endl;

  if (args.HasOption("port")) {
    port = args.GetOptionString("port").AsInteger();
  } else {
    port = 1; // default to a real telephone
  }

  OpalIxJDevice xJack;
  if (!OpenDevice(xJack, args.GetOptionString('q'),port))
    return;

  // ring handset
  if (port == 1) {
    xJack.RingLine(OpalIxJDevice::POTSLine, 0x33);

    // wait for answer
    cout << "Waiting for phone to go offhook...";
    cout.flush();

    while (!xJack.IsLineOffHook(OpalIxJDevice::POTSLine))
      Sleep(100);

    cout << endl;
  }


  // start codecs
  if (!xJack.SetReadCodec (OpalIxJDevice::POTSLine,
                           isPCM ? RTP_DataFrame::L16_Mono : RTP_DataFrame::G7231)) {
    PError << "error: error during SetReadCodec" << endl;
    return;
  }
  if (!xJack.SetWriteCodec(OpalIxJDevice::POTSLine,
                           isPCM ? RTP_DataFrame::L16_Mono : RTP_DataFrame::G7231)) {
    PError << "error: error during SetWriteCodec" << endl;
    return;
  }

  // determine the write buffer size
  PINDEX bufferSize;
  if (isPCM)
    bufferSize = xJack.GetReadFrameSize(OpalIxJDevice::POTSLine);
  else
    bufferSize = G7231_BUFFER_SIZE;

  // allocate a buffer
  PBYTEArray buffer;
  buffer.SetSize(bufferSize);


  // Create the console thread.
  PMutex enter;
  BOOL enter_pressed = FALSE;
  PThread * consolethread;
  consolethread = new ConsoleThread( &enter, &enter_pressed);

  cout << "Playback Started." << endl << "To stop playback " ;
  if (port == 1)
    cout << "put the phone back onhook or ";
  cout << "press Enter" << endl;

  // start playing
  for (;;) {

    // stop if we are using POTS and the telephone handset it put down
    if ((port == 1) && (!xJack.IsLineOffHook(OpalIxJDevice::POTSLine))) {
      break;
    }

    // stop if the user pressed Enter on the console
    enter.Wait();
    if (enter_pressed == TRUE) {
      enter.Signal();
      break;
    }
    enter.Signal();

    PINDEX count = 0;
    if (isPCM) {
      if (!playFile->Read(buffer.GetPointer(), bufferSize))
        break;
    } else {
      if (!playFile->Read(buffer.GetPointer(), 1)) 
        break;
      count++;
      static const int frameLen[] = { 24, 20, 4, 1 };
      if (!playFile->Read(buffer.GetPointer()+1, frameLen[buffer[0]&3] - 1))
        break;
    }
    count += playFile->GetLastReadCount();

    PINDEX written;
    if (!xJack.WriteFrame(OpalIxJDevice::POTSLine, buffer.GetPointer(), count, written)) {
      PError << "error: error during WriteFrame" << endl;
      return;
    }
  }

  // stop recording;
  xJack.StopReadCodec (OpalIxJDevice::POTSLine);
  xJack.StopWriteCodec(OpalIxJDevice::POTSLine);

  // close the file
  playFile->Close();
  delete playFile;
}


#endif


syntax highlighted by Code2HTML, v. 0.9.1