/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#endif

#include <vector>

#include "gflags/gflags.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/call/rtc_event_log.h"
#include "webrtc/engine_configurations.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/test/channel_transport/include/channel_transport.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/trace_to_stderr.h"
#include "webrtc/voice_engine/include/voe_audio_processing.h"
#include "webrtc/voice_engine/include/voe_base.h"
#include "webrtc/voice_engine/include/voe_codec.h"
#include "webrtc/voice_engine/include/voe_dtmf.h"
#include "webrtc/voice_engine/include/voe_errors.h"
#include "webrtc/voice_engine/include/voe_external_media.h"
#include "webrtc/voice_engine/include/voe_file.h"
#include "webrtc/voice_engine/include/voe_hardware.h"
#include "webrtc/voice_engine/include/voe_neteq_stats.h"
#include "webrtc/voice_engine/include/voe_network.h"
#include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
#include "webrtc/voice_engine/include/voe_video_sync.h"
#include "webrtc/voice_engine/include/voe_volume_control.h"

DEFINE_bool(use_log_file, false,
    "Output logs to a file; by default they will be printed to stderr.");

using namespace webrtc;
using namespace test;

#define VALIDATE                                           \
  if (res != 0) {                                          \
    printf("*** Error at line %i \n", __LINE__);           \
    printf("*** Error code = %i \n", base1->LastError());  \
  }

VoiceEngine* m_voe = NULL;
VoEBase* base1 = NULL;
VoECodec* codec = NULL;
VoEVolumeControl* volume = NULL;
VoEDtmf* dtmf = NULL;
VoERTP_RTCP* rtp_rtcp = NULL;
VoEAudioProcessing* apm = NULL;
VoENetwork* netw = NULL;
VoEFile* file = NULL;
VoEVideoSync* vsync = NULL;
VoEHardware* hardware = NULL;
VoEExternalMedia* xmedia = NULL;
VoENetEqStats* neteqst = NULL;

void RunTest(std::string out_path);

class MyObserver : public VoiceEngineObserver {
 public:
   virtual void CallbackOnError(int channel, int err_code);
};

void MyObserver::CallbackOnError(int channel, int err_code) {
  // Add printf for other error codes here
  if (err_code == VE_TYPING_NOISE_WARNING) {
    printf("  TYPING NOISE DETECTED \n");
  } else if (err_code == VE_TYPING_NOISE_OFF_WARNING) {
    printf("  TYPING NOISE OFF DETECTED \n");
  } else if (err_code == VE_RECEIVE_PACKET_TIMEOUT) {
    printf("  RECEIVE PACKET TIMEOUT \n");
  } else if (err_code == VE_PACKET_RECEIPT_RESTARTED) {
    printf("  PACKET RECEIPT RESTARTED \n");
  } else if (err_code == VE_RUNTIME_PLAY_WARNING) {
    printf("  RUNTIME PLAY WARNING \n");
  } else if (err_code == VE_RUNTIME_REC_WARNING) {
    printf("  RUNTIME RECORD WARNING \n");
  } else if (err_code == VE_SATURATION_WARNING) {
    printf("  SATURATION WARNING \n");
  } else if (err_code == VE_RUNTIME_PLAY_ERROR) {
    printf("  RUNTIME PLAY ERROR \n");
  } else if (err_code == VE_RUNTIME_REC_ERROR) {
    printf("  RUNTIME RECORD ERROR \n");
  } else if (err_code == VE_REC_DEVICE_REMOVED) {
    printf("  RECORD DEVICE REMOVED \n");
  }
}

void SetStereoIfOpus(bool use_stereo, CodecInst* codec_params) {
  if (strncmp(codec_params->plname, "opus", 4) == 0) {
    if (use_stereo)
      codec_params->channels = 2;
    else
      codec_params->channels = 1;
  }
}

void PrintCodecs(bool opus_stereo) {
  CodecInst codec_params;
  for (int i = 0; i < codec->NumOfCodecs(); ++i) {
    int res = codec->GetCodec(i, codec_params);
    VALIDATE;
    SetStereoIfOpus(opus_stereo, &codec_params);
    printf("%2d. %3d  %s/%d/%d \n", i, codec_params.pltype, codec_params.plname,
           codec_params.plfreq, codec_params.channels);
  }
}

int main(int argc, char** argv) {
  google::ParseCommandLineFlags(&argc, &argv, true);

  int res = 0;

  printf("Test started \n");

  m_voe = VoiceEngine::Create();
  base1 = VoEBase::GetInterface(m_voe);
  codec = VoECodec::GetInterface(m_voe);
  apm = VoEAudioProcessing::GetInterface(m_voe);
  volume = VoEVolumeControl::GetInterface(m_voe);
  dtmf = VoEDtmf::GetInterface(m_voe);
  rtp_rtcp = VoERTP_RTCP::GetInterface(m_voe);
  netw = VoENetwork::GetInterface(m_voe);
  file = VoEFile::GetInterface(m_voe);
  vsync = VoEVideoSync::GetInterface(m_voe);
  hardware = VoEHardware::GetInterface(m_voe);
  xmedia = VoEExternalMedia::GetInterface(m_voe);
  neteqst = VoENetEqStats::GetInterface(m_voe);

  MyObserver my_observer;

  rtc::scoped_ptr<test::TraceToStderr> trace_to_stderr;
  if (!FLAGS_use_log_file) {
    trace_to_stderr.reset(new test::TraceToStderr);
  } else {
    const std::string trace_filename = test::OutputPath() + "webrtc_trace.txt";
    VoiceEngine::SetTraceFilter(kTraceAll);
    res = VoiceEngine::SetTraceFile(trace_filename.c_str());
    VALIDATE;
    res = VoiceEngine::SetTraceCallback(NULL);
    VALIDATE;
    printf("Outputting logs to file: %s\n", trace_filename.c_str());
  }

  printf("Init\n");
  res = base1->Init();
  if (res != 0) {
    printf("\nError calling Init: %d\n", base1->LastError());
    fflush(NULL);
    exit(1);
  }

  res = base1->RegisterVoiceEngineObserver(my_observer);
  VALIDATE;

  printf("Version\n");
  char tmp[1024];
  res = base1->GetVersion(tmp);
  VALIDATE;
  printf("%s\n", tmp);

  RunTest(test::OutputPath());

  printf("Terminate \n");

  base1->DeRegisterVoiceEngineObserver();

  res = base1->Terminate();
  VALIDATE;

  if (base1)
    base1->Release();

  if (codec)
    codec->Release();

  if (volume)
    volume->Release();

  if (dtmf)
    dtmf->Release();

  if (rtp_rtcp)
    rtp_rtcp->Release();

  if (apm)
    apm->Release();

  if (netw)
    netw->Release();

  if (file)
    file->Release();

  if (vsync)
    vsync->Release();

  if (hardware)
    hardware->Release();

  if (xmedia)
    xmedia->Release();

  if (neteqst)
    neteqst->Release();

  VoiceEngine::Delete(m_voe);

  return 0;
}

void RunTest(std::string out_path) {
  int chan, res;
  CodecInst cinst;
  bool enable_aec = false;
  bool enable_agc = false;
  bool enable_rx_agc = false;
  bool enable_cng = false;
  bool enable_ns = false;
  bool enable_rx_ns = false;
  bool typing_detection = false;
  bool muted = false;
  bool opus_stereo = false;
  bool opus_dtx = false;
  bool experimental_ns_enabled = false;
  bool debug_recording_started = false;

#if defined(WEBRTC_ANDROID)
  std::string resource_path = "/sdcard/";
#else
  std::string resource_path = webrtc::test::ProjectRootPath();
  if (resource_path == webrtc::test::kCannotFindProjectRootDir) {
    printf("*** Unable to get project root directory. "
           "File playing may fail. ***\n");
    // Fall back to the current directory.
    resource_path = "./";
  } else {
    resource_path += "data/voice_engine/";
  }
#endif
  const std::string audio_filename = resource_path + "audio_long16.pcm";

  const std::string play_filename = out_path + "recorded_playout.pcm";
  const std::string mic_filename = out_path + "recorded_mic.pcm";

  chan = base1->CreateChannel();
  if (chan < 0) {
    printf("************ Error code = %i\n", base1->LastError());
    fflush(NULL);
  }

  VoiceChannelTransport* voice_channel_transport(
      new VoiceChannelTransport(netw, chan));

  char ip[64];
  printf("1. 127.0.0.1 \n");
  printf("2. Specify IP \n");
  int ip_selection;
  ASSERT_EQ(1, scanf("%i", &ip_selection));

  if (ip_selection == 1) {
    strcpy(ip, "127.0.0.1");
  } else {
    printf("Specify remote IP: ");
    ASSERT_EQ(1, scanf("%s", ip));
  }

  int rPort;
  printf("Specify remote port (1=1234): ");
  ASSERT_EQ(1, scanf("%i", &rPort));
  if (1 == rPort)
    rPort = 1234;
  printf("Set Send port \n");

  printf("Set Send IP \n");
  res = voice_channel_transport->SetSendDestination(ip, rPort);
  VALIDATE;

  int lPort;
  printf("Specify local port (1=1234): ");
  ASSERT_EQ(1, scanf("%i", &lPort));
  if (1 == lPort)
    lPort = 1234;
  printf("Set Rec Port \n");

  res = voice_channel_transport->SetLocalReceiver(lPort);
  VALIDATE;

  printf("\n");
  PrintCodecs(opus_stereo);
  printf("Select send codec: ");
  int codec_selection;
  ASSERT_EQ(1, scanf("%i", &codec_selection));
  codec->GetCodec(codec_selection, cinst);

  printf("Set primary codec\n");
  SetStereoIfOpus(opus_stereo, &cinst);
  res = codec->SetSendCodec(chan, cinst);
  VALIDATE;

  const int kMaxNumChannels = 8;
  int channel_index = 0;
  std::vector<int> channels(kMaxNumChannels);
  std::vector<VoiceChannelTransport*> voice_channel_transports(kMaxNumChannels);

  for (int i = 0; i < kMaxNumChannels; ++i) {
    channels[i] = base1->CreateChannel();
    int port = rPort + (i + 1) * 2;

    voice_channel_transports[i] = new VoiceChannelTransport(netw, channels[i]);

    res = voice_channel_transports[i]->SetSendDestination(ip, port);
    VALIDATE;
    res = voice_channel_transports[i]->SetLocalReceiver(port);
    VALIDATE;
    res = codec->SetSendCodec(channels[i], cinst);
    VALIDATE;
  }

  // Call loop
  bool newcall = true;
  while (newcall) {
    int rd(-1), pd(-1);
    res = hardware->GetNumOfRecordingDevices(rd);
    VALIDATE;
    res = hardware->GetNumOfPlayoutDevices(pd);
    VALIDATE;

    char dn[128] = { 0 };
    char guid[128] = { 0 };
    printf("\nPlayout devices (%d): \n", pd);
    for (int j = 0; j < pd; ++j) {
      res = hardware->GetPlayoutDeviceName(j, dn, guid);
      VALIDATE;
      printf("  %d: %s \n", j, dn);
    }

    printf("Recording devices (%d): \n", rd);
    for (int j = 0; j < rd; ++j) {
      res = hardware->GetRecordingDeviceName(j, dn, guid);
      VALIDATE;
      printf("  %d: %s \n", j, dn);
    }

    printf("Select playout device: ");
    ASSERT_EQ(1, scanf("%d", &pd));
    res = hardware->SetPlayoutDevice(pd);
    VALIDATE;
    printf("Select recording device: ");
    ASSERT_EQ(1, scanf("%d", &rd));
    printf("Setting sound devices \n");
    res = hardware->SetRecordingDevice(rd);
    VALIDATE;

    res = codec->SetVADStatus(0, enable_cng);
    VALIDATE;

    res = apm->SetAgcStatus(enable_agc);
    VALIDATE;

    res = apm->SetEcStatus(enable_aec);
    VALIDATE;

    res = apm->SetNsStatus(enable_ns);
    VALIDATE;

    printf("\n1. Send, listen and playout \n");
    printf("2. Send only \n");
    printf("3. Listen and playout only \n");
    printf("Select transfer mode: ");
    int call_selection;
    ASSERT_EQ(1, scanf("%i", &call_selection));
    const bool send = !(call_selection == 3);
    const bool receive = !(call_selection == 2);

    if (receive) {
#ifndef EXTERNAL_TRANSPORT
      printf("Start Listen \n");
      res = base1->StartReceive(chan);
      VALIDATE;
#endif

      printf("Start Playout \n");
      res = base1->StartPlayout(chan);
      VALIDATE;
    }

    if (send) {
      printf("Start Send \n");
      res = base1->StartSend(chan);
      VALIDATE;
    }

    printf("Getting mic volume \n");
    unsigned int vol = 999;
    res = volume->GetMicVolume(vol);
    VALIDATE;
    if ((vol > 255) || (vol < 1)) {
      printf("\n****ERROR in GetMicVolume");
    }

    int forever = 1;
    while (forever) {
      printf("\nSelect codec\n");
      PrintCodecs(opus_stereo);
      printf("\nOther actions\n");
      const int num_codecs = codec->NumOfCodecs();
      int option_index = num_codecs;
      printf("%i. Toggle CNG\n", option_index++);
      printf("%i. Toggle AGC\n", option_index++);
      printf("%i. Toggle NS\n", option_index++);
      printf("%i. Toggle experimental NS\n", option_index++);
      printf("%i. Toggle EC\n", option_index++);
      printf("%i. Select AEC\n", option_index++);
      printf("%i. Select AECM\n", option_index++);
      printf("%i. Get speaker volume\n", option_index++);
      printf("%i. Set speaker volume\n", option_index++);
      printf("%i. Get microphone volume\n", option_index++);
      printf("%i. Set microphone volume\n", option_index++);
      printf("%i. Play local file (audio_long16.pcm) \n", option_index++);
      printf("%i. Change playout device \n", option_index++);
      printf("%i. Change recording device \n", option_index++);
      printf("%i. Toggle receive-side AGC \n", option_index++);
      printf("%i. Toggle receive-side NS \n", option_index++);
      printf("%i. AGC status \n", option_index++);
      printf("%i. Toggle microphone mute \n", option_index++);
      printf("%i. Get last error code \n", option_index++);
      printf("%i. Toggle typing detection \n",
             option_index++);
      printf("%i. Record a PCM file \n", option_index++);
      printf("%i. Play a previously recorded PCM file locally \n",
             option_index++);
      printf("%i. Play a previously recorded PCM file as microphone \n",
             option_index++);
      printf("%i. Add an additional file-playing channel \n", option_index++);
      printf("%i. Remove a file-playing channel \n", option_index++);
      printf("%i. Toggle Opus stereo (Opus must be selected again to apply "
             "the setting) \n", option_index++);
      printf("%i. Set Opus maximum playback rate \n", option_index++);
      printf("%i. Toggle Opus DTX \n", option_index++);
      printf("%i. Set bit rate (only take effect on codecs that allow the "
             "change) \n", option_index++);
      printf("%i. Toggle AECdump recording \n", option_index++);
      printf("%i. Record RtcEventLog file of 30 seconds \n", option_index++);

      printf("Select action or %i to stop the call: ", option_index);
      int option_selection;
      ASSERT_EQ(1, scanf("%i", &option_selection));

      option_index = num_codecs;
      if (option_selection < option_index) {
        res = codec->GetCodec(option_selection, cinst);
        VALIDATE;
        if (strcmp(cinst.plname, "red") == 0) {
          printf("Enabling RED\n");
          res = rtp_rtcp->SetREDStatus(chan, true, cinst.pltype);
        } else {
          SetStereoIfOpus(opus_stereo, &cinst);
          printf("Set primary codec\n");
          res = codec->SetSendCodec(chan, cinst);
        }
        VALIDATE;
      } else if (option_selection == option_index++) {
        enable_cng = !enable_cng;
        res = codec->SetVADStatus(0, enable_cng);
        VALIDATE;
        if (enable_cng)
          printf("\n CNG is now on! \n");
        else
          printf("\n CNG is now off! \n");
      } else if (option_selection == option_index++) {
        enable_agc = !enable_agc;
        res = apm->SetAgcStatus(enable_agc);
        VALIDATE;
        if (enable_agc)
          printf("\n AGC is now on! \n");
        else
          printf("\n AGC is now off! \n");
      } else if (option_selection == option_index++) {
        enable_ns = !enable_ns;
        res = apm->SetNsStatus(enable_ns);
        VALIDATE;
        if (enable_ns)
          printf("\n NS is now on! \n");
        else
          printf("\n NS is now off! \n");
      } else if (option_selection == option_index++) {
        experimental_ns_enabled = !experimental_ns_enabled;
        Config config;
        config.Set<ExperimentalNs>(new ExperimentalNs(experimental_ns_enabled));
        base1->audio_processing()->SetExtraOptions(config);
        if (experimental_ns_enabled) {
          printf("\n Experimental NS is now on!\n");
        } else {
          printf("\n Experimental NS is now off!\n");
        }
      } else if (option_selection == option_index++) {
        enable_aec = !enable_aec;
        res = apm->SetEcStatus(enable_aec, kEcUnchanged);
        VALIDATE;
        if (enable_aec)
          printf("\n Echo control is now on! \n");
        else
          printf("\n Echo control is now off! \n");
      } else if (option_selection == option_index++) {
        res = apm->SetEcStatus(enable_aec, kEcAec);
        VALIDATE;
        printf("\n AEC selected! \n");
        if (enable_aec)
          printf(" (Echo control is on)\n");
        else
          printf(" (Echo control is off)\n");
      } else if (option_selection == option_index++) {
        res = apm->SetEcStatus(enable_aec, kEcAecm);
        VALIDATE;
        printf("\n AECM selected! \n");
        if (enable_aec)
          printf(" (Echo control is on)\n");
        else
          printf(" (Echo control is off)\n");
      } else if (option_selection == option_index++) {
        unsigned vol(0);
        res = volume->GetSpeakerVolume(vol);
        VALIDATE;
        printf("\n Speaker Volume is %d \n", vol);
      } else if (option_selection == option_index++) {
        printf("Level: ");
        int level;
        ASSERT_EQ(1, scanf("%i", &level));
        res = volume->SetSpeakerVolume(level);
        VALIDATE;
      } else if (option_selection == option_index++) {
        unsigned vol(0);
        res = volume->GetMicVolume(vol);
        VALIDATE;
        printf("\n Microphone Volume is %d \n", vol);
      } else if (option_selection == option_index++) {
        printf("Level: ");
        int level;
        ASSERT_EQ(1, scanf("%i", &level));
        res = volume->SetMicVolume(level);
        VALIDATE;
      } else if (option_selection == option_index++) {
        res = file->StartPlayingFileLocally(0, audio_filename.c_str());
        VALIDATE;
      } else if (option_selection == option_index++) {
        // change the playout device with current call
        int num_pd(-1);
        res = hardware->GetNumOfPlayoutDevices(num_pd);
        VALIDATE;

        char dn[128] = { 0 };
        char guid[128] = { 0 };

        printf("\nPlayout devices (%d): \n", num_pd);
        for (int i = 0; i < num_pd; ++i) {
          res = hardware->GetPlayoutDeviceName(i, dn, guid);
          VALIDATE;
          printf("  %d: %s \n", i, dn);
        }
        printf("Select playout device: ");
        ASSERT_EQ(1, scanf("%d", &num_pd));
        // Will use plughw for hardware devices
        res = hardware->SetPlayoutDevice(num_pd);
        VALIDATE;
      } else if (option_selection == option_index++) {
        // change the recording device with current call
        int num_rd(-1);

        res = hardware->GetNumOfRecordingDevices(num_rd);
        VALIDATE;

        char dn[128] = { 0 };
        char guid[128] = { 0 };

        printf("Recording devices (%d): \n", num_rd);
        for (int i = 0; i < num_rd; ++i) {
          res = hardware->GetRecordingDeviceName(i, dn, guid);
          VALIDATE;
          printf("  %d: %s \n", i, dn);
        }

        printf("Select recording device: ");
        ASSERT_EQ(1, scanf("%d", &num_rd));
        printf("Setting sound devices \n");
        // Will use plughw for hardware devices
        res = hardware->SetRecordingDevice(num_rd);
        VALIDATE;
      } else if (option_selection == option_index++) {
        // Remote AGC
        enable_rx_agc = !enable_rx_agc;
        res = apm->SetRxAgcStatus(chan, enable_rx_agc);
        VALIDATE;
        if (enable_rx_agc)
          printf("\n Receive-side AGC is now on! \n");
        else
          printf("\n Receive-side AGC is now off! \n");
      } else if (option_selection == option_index++) {
        // Remote NS
        enable_rx_ns = !enable_rx_ns;
        res = apm->SetRxNsStatus(chan, enable_rx_ns);
        VALIDATE;
        if (enable_rx_ns)
          printf("\n Receive-side NS is now on! \n");
        else
          printf("\n Receive-side NS is now off! \n");
      } else if (option_selection == option_index++) {
        AgcModes agcmode;
        bool enable;
        res = apm->GetAgcStatus(enable, agcmode);
        VALIDATE
            printf("\n AGC enable is %d, mode is %d \n", enable, agcmode);
      } else if (option_selection == option_index++) {
        // Toggle Mute on Microphone
        res = volume->GetInputMute(chan, muted);
        VALIDATE;
        muted = !muted;
        res = volume->SetInputMute(chan, muted);
        VALIDATE;
        if (muted)
          printf("\n Microphone is now on mute! \n");
        else
          printf("\n Microphone is no longer on mute! \n");
      } else if (option_selection == option_index++) {
        // Get the last error code and print to screen
        int err_code = 0;
        err_code = base1->LastError();
        if (err_code != -1)
          printf("\n The last error code was %i.\n", err_code);
      } else if (option_selection == option_index++) {
        typing_detection= !typing_detection;
        res = apm->SetTypingDetectionStatus(typing_detection);
        VALIDATE;
        if (typing_detection)
          printf("\n Typing detection is now on!\n");
        else
          printf("\n Typing detection is now off!\n");
      } else if (option_selection == option_index++) {
        int stop_record = 1;
        int file_source = 1;
        printf("\n Select source of recorded file. ");
        printf("\n 1. Record from microphone to file ");
        printf("\n 2. Record from playout to file ");
        printf("\n Enter your selection: \n");
        ASSERT_EQ(1, scanf("%i", &file_source));
        if (file_source == 1) {
          printf("\n Start recording microphone as %s \n",
                 mic_filename.c_str());
          res = file->StartRecordingMicrophone(mic_filename.c_str());
          VALIDATE;
        } else {
          printf("\n Start recording playout as %s \n", play_filename.c_str());
          res = file->StartRecordingPlayout(chan, play_filename.c_str());
          VALIDATE;
        }
        while (stop_record != 0) {
          printf("\n Type 0 to stop recording file \n");
          ASSERT_EQ(1, scanf("%i", &stop_record));
        }
        if (file_source == 1) {
          res = file->StopRecordingMicrophone();
          VALIDATE;
        } else {
          res = file->StopRecordingPlayout(chan);
          VALIDATE;
        }
        printf("\n File finished recording \n");
      } else if (option_selection == option_index++) {
        int file_type = 1;
        int stop_play = 1;
        printf("\n Select a file to play locally in a loop.");
        printf("\n 1. Play %s", mic_filename.c_str());
        printf("\n 2. Play %s", play_filename.c_str());
        printf("\n Enter your selection\n");
        ASSERT_EQ(1, scanf("%i", &file_type));
        if (file_type == 1)  {
          printf("\n Start playing %s locally in a loop\n",
                 mic_filename.c_str());
          res = file->StartPlayingFileLocally(chan, mic_filename.c_str(), true);
          VALIDATE;
        } else {
          printf("\n Start playing %s locally in a loop\n",
                 play_filename.c_str());
          res = file->StartPlayingFileLocally(chan, play_filename.c_str(),
                                              true);
          VALIDATE;
        }
        while (stop_play != 0) {
          printf("\n Type 0 to stop playing file\n");
          ASSERT_EQ(1, scanf("%i", &stop_play));
        }
        res = file->StopPlayingFileLocally(chan);
        VALIDATE;
      } else if (option_selection == option_index++) {
        int file_type = 1;
        int stop_play = 1;
        printf("\n Select a file to play as microphone in a loop.");
        printf("\n 1. Play %s", mic_filename.c_str());
        printf("\n 2. Play %s", play_filename.c_str());
        printf("\n Enter your selection\n");
        ASSERT_EQ(1, scanf("%i", &file_type));
        if (file_type == 1)  {
          printf("\n Start playing %s as mic in a loop\n",
                 mic_filename.c_str());
          res = file->StartPlayingFileAsMicrophone(chan, mic_filename.c_str(),
                                                   true);
          VALIDATE;
        } else {
          printf("\n Start playing %s as mic in a loop\n",
                 play_filename.c_str());
          res = file->StartPlayingFileAsMicrophone(chan, play_filename.c_str(),
                                                   true);
          VALIDATE;
        }
        while (stop_play != 0) {
          printf("\n Type 0 to stop playing file\n");
          ASSERT_EQ(1, scanf("%i", &stop_play));
        }
        res = file->StopPlayingFileAsMicrophone(chan);
        VALIDATE;
      } else if (option_selection == option_index++) {
        if (channel_index < kMaxNumChannels) {
          res = base1->StartReceive(channels[channel_index]);
          VALIDATE;
          res = base1->StartPlayout(channels[channel_index]);
          VALIDATE;
          res = base1->StartSend(channels[channel_index]);
          VALIDATE;
          res = file->StartPlayingFileAsMicrophone(channels[channel_index],
                                                   audio_filename.c_str(),
                                                   true,
                                                   false);
          VALIDATE;
          channel_index++;
          printf("Using %d additional channels\n", channel_index);
        } else {
          printf("Max number of channels reached\n");
        }
      } else if (option_selection == option_index++) {
        if (channel_index > 0) {
          channel_index--;
          res = file->StopPlayingFileAsMicrophone(channels[channel_index]);
          VALIDATE;
          res = base1->StopSend(channels[channel_index]);
          VALIDATE;
          res = base1->StopPlayout(channels[channel_index]);
          VALIDATE;
          res = base1->StopReceive(channels[channel_index]);
          VALIDATE;
          printf("Using %d additional channels\n", channel_index);
        } else {
          printf("All additional channels stopped\n");
        }
      } else if (option_selection == option_index++) {
        opus_stereo = !opus_stereo;
        if (opus_stereo)
          printf("\n Opus stereo enabled (select Opus again to apply the "
                 "setting). \n");
        else
          printf("\n Opus mono enabled (select Opus again to apply the "
                 "setting). \n");
      } else if (option_selection == option_index++) {
        printf("\n Input maxium playback rate in Hz: ");
        int max_playback_rate;
        ASSERT_EQ(1, scanf("%i", &max_playback_rate));
        res = codec->SetOpusMaxPlaybackRate(chan, max_playback_rate);
        VALIDATE;
      } else if (option_selection == option_index++) {
        opus_dtx = !opus_dtx;
        res = codec->SetOpusDtx(chan, opus_dtx);
        VALIDATE;
        printf("Opus DTX %s.\n", opus_dtx ? "enabled" : "disabled");
      } else if (option_selection == option_index++) {
        res = codec->GetSendCodec(chan, cinst);
        VALIDATE;
        printf("Current bit rate is %i bps, set to: ", cinst.rate);
        int new_bitrate_bps;
        ASSERT_EQ(1, scanf("%i", &new_bitrate_bps));
        res = codec->SetBitRate(chan, new_bitrate_bps);
        VALIDATE;
      } else if (option_selection == option_index++) {
        const char* kDebugFileName = "audio.aecdump";
        if (debug_recording_started) {
          apm->StopDebugRecording();
          printf("Debug recording named %s stopped\n", kDebugFileName);
        } else {
          apm->StartDebugRecording(kDebugFileName);
          printf("Debug recording named %s started\n", kDebugFileName);
        }
        debug_recording_started = !debug_recording_started;
      } else if (option_selection == option_index++) {
        const char* kDebugFileName = "eventlog.rel";
        codec->GetEventLog()->StartLogging(kDebugFileName, 30000);
      } else {
        break;
      }
    }

    if (debug_recording_started) {
      apm->StopDebugRecording();
    }

    if (send) {
      printf("Stop Send \n");
      res = base1->StopSend(chan);
      VALIDATE;
    }

    if (receive) {
      printf("Stop Playout \n");
      res = base1->StopPlayout(chan);
      VALIDATE;

#ifndef EXTERNAL_TRANSPORT
      printf("Stop Listen \n");
      res = base1->StopReceive(chan);
      VALIDATE;
#endif
    }

    while (channel_index > 0) {
      --channel_index;
      res = file->StopPlayingFileAsMicrophone(channels[channel_index]);
      VALIDATE;
      res = base1->StopSend(channels[channel_index]);
      VALIDATE;
      res = base1->StopPlayout(channels[channel_index]);
      VALIDATE;
      res = base1->StopReceive(channels[channel_index]);
      VALIDATE;
    }

    printf("\n1. New call \n");
    printf("2. Quit \n");
    printf("Select action: ");
    int end_option;
    ASSERT_EQ(1, scanf("%i", &end_option));
    newcall = (end_option == 1);
    // Call loop
  }

  // Transports should be deleted before channel deletion.
  delete voice_channel_transport;
  for (int i = 0; i < kMaxNumChannels; ++i) {
    delete voice_channel_transports[i];
    voice_channel_transports[i] = NULL;
  }

  printf("Delete channels \n");
  res = base1->DeleteChannel(chan);
  VALIDATE;

  for (int i = 0; i < kMaxNumChannels; ++i) {
    res = base1->DeleteChannel(channels[i]);
    VALIDATE;
  }
}
