<commit_before>#include <cmath>

#include "ATKStereoCompressor.h"
#include "IPlug_include_in_plug_src.h"
#include "IControl.h"
#include "resource.h"

const int kNumPrograms = 1;

enum EParams
{
  kMiddleside = 0, // do we process data with middle/side or plain L/R ?
  kLinkChannels,
  kActivateChannel1,
  kActivateChannel2,
  kAttack1,
  kRelease1,
  kThreshold1,
  kSlope1,
  kSoftness1,
  kMakeup1,
  kAttack2,
  kRelease2,
  kThreshold2,
  kSlope2,
  kSoftness2,
  kMakeup2,
  kNumParams
};

enum ELayout
{
  kWidth = GUI_WIDTH,
  kHeight = GUI_HEIGHT,

  kAttackX = 25,
  kAttackY = 31,
  kReleaseX = 94,
  kReleaseY = 31,
  kThresholdX = 163,
  kThresholdY = 31,
  kSlopeX = 232,
  kSlopeY = 31,
  kSoftnessX = 301,
  kSoftnessY = 31,
  kMakeupX = 370,
  kMakeupY = 31,
  kKnobFrames = 43
};

ATKStereoCompressor::ATKStereoCompressor(IPlugInstanceInfo instanceInfo)
  :	IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo),
  inLFilter(NULL, 1, 0, false), inRFilter(NULL, 1, 0, false), outLFilter(NULL, 1, 0, false), outRFilter(NULL, 1, 0, false)
{
  TRACE;

  //arguments are: name, defaultVal, minVal, maxVal, step, label
  GetParam(kMiddleside)->InitBool("Middle/Side processing", 0, "");
  GetParam(kLinkChannels)->InitBool("Link channels", 0, "");
  GetParam(kActivateChannel1)->InitBool("Enable Channel 1", 1, "");
  GetParam(kActivateChannel1)->InitBool("Enable Channel 2", 1, "");

  GetParam(kAttack1)->InitDouble("Attack ch1", 10., 1., 100.0, 0.1, "ms");
  GetParam(kAttack1)->SetShape(2.);
  GetParam(kRelease1)->InitDouble("Release ch1", 10, 1., 100.0, 0.1, "ms");
  GetParam(kRelease1)->SetShape(2.);
  GetParam(kThreshold1)->InitDouble("Threshold ch1", 0., -40., 0.0, 0.1, "dB"); // threshold is actually power
  GetParam(kThreshold1)->SetShape(2.);
  GetParam(kSlope1)->InitDouble("Slope ch1", 2., 1, 100, 1, "-");
  GetParam(kSlope1)->SetShape(2.);
  GetParam(kSoftness1)->InitDouble("Softness ch1", -2, -4, 0, 0.1, "-");
  GetParam(kSoftness1)->SetShape(2.);
  GetParam(kMakeup1)->InitDouble("Makeup Gain ch1", 0, 0, 40, 0.1, "-"); // Makeup is expressed in amplitude
  GetParam(kMakeup1)->SetShape(2.);
  GetParam(kAttack2)->InitDouble("Attack ch2", 10., 1., 100.0, 0.1, "ms");
  GetParam(kAttack2)->SetShape(2.);
  GetParam(kRelease2)->InitDouble("Release ch2", 10, 1., 100.0, 0.1, "ms");
  GetParam(kRelease2)->SetShape(2.);
  GetParam(kThreshold2)->InitDouble("Threshold ch2", 0., -40., 0.0, 0.1, "dB"); // threshold is actually power
  GetParam(kThreshold2)->SetShape(2.);
  GetParam(kSlope2)->InitDouble("Slope ch2", 2., 1, 100, 1, "-");
  GetParam(kSlope2)->SetShape(2.);
  GetParam(kSoftness2)->InitDouble("Softness ch2", -2, -4, 0, 0.1, "-");
  GetParam(kSoftness2)->SetShape(2.);
  GetParam(kMakeup2)->InitDouble("Makeup Gain ch2", 0, 0, 40, 0.1, "-"); // Makeup is expressed in amplitude
  GetParam(kMakeup2)->SetShape(2.);

  IGraphics* pGraphics = MakeGraphics(this, kWidth, kHeight);
  pGraphics->AttachBackground(COMPRESSOR_ID, COMPRESSOR_FN);

  IBitmap knob = pGraphics->LoadIBitmap(KNOB_ID, KNOB_FN, kKnobFrames);

  pGraphics->AttachControl(new IKnobMultiControl(this, kAttackX, kAttackY, kAttack1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kReleaseX, kReleaseY, kRelease1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kThresholdX, kThresholdY, kThreshold1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kSlopeX, kSlopeY, kSlope1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kSoftnessX, kSoftnessY, kSoftness1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kMakeupX, kMakeupY, kMakeup1, &knob));

  AttachGraphics(pGraphics);

  //MakePreset("preset 1", ... );
  MakeDefaultPreset((char *) "-", kNumPrograms);

  volumemergeFilter.set_volume(.5);
  endpoint.add_filter(&outLFilter);
  endpoint.add_filter(&outRFilter);

  powerFilter1.set_input_port(0, &inLFilter, 0);
  gainCompressorFilter1.set_input_port(0, &powerFilter1, 0);
  attackReleaseFilter1.set_input_port(0, &gainCompressorFilter1, 0);
  applyGainFilter1.set_input_port(0, &attackReleaseFilter1, 0);
  applyGainFilter1.set_input_port(1, &inLFilter, 0);
  makeupFilter1.set_input_port(0, &applyGainFilter1, 0);
  outLFilter.set_input_port(0, &makeupFilter1, 0);

  powerFilter2.set_input_port(0, &inRFilter, 0);
  gainCompressorFilter2.set_input_port(0, &powerFilter2, 0);
  attackReleaseFilter2.set_input_port(0, &gainCompressorFilter2, 0);
  applyGainFilter2.set_input_port(0, &attackReleaseFilter2, 0);
  applyGainFilter2.set_input_port(1, &inRFilter, 0);
  makeupFilter2.set_input_port(0, &applyGainFilter2, 0);
  outRFilter.set_input_port(0, &makeupFilter2, 0);

  Reset();
}

ATKStereoCompressor::~ATKStereoCompressor() {}

void ATKStereoCompressor::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
{
  // Mutex is already locked for us.

  inLFilter.set_pointer(inputs[0], nFrames);
  outLFilter.set_pointer(outputs[0], nFrames);
  inRFilter.set_pointer(inputs[1], nFrames);
  outRFilter.set_pointer(outputs[1], nFrames);
  endpoint.process(nFrames);
}

void ATKStereoCompressor::Reset()
{
  TRACE;
  IMutexLock lock(this);

  int sampling_rate = GetSampleRate();

  if (sampling_rate != endpoint.get_input_sampling_rate())
  {
    inLFilter.set_input_sampling_rate(sampling_rate);
    inLFilter.set_output_sampling_rate(sampling_rate);
    inRFilter.set_input_sampling_rate(sampling_rate);
    inRFilter.set_output_sampling_rate(sampling_rate);
    outLFilter.set_input_sampling_rate(sampling_rate);
    outLFilter.set_output_sampling_rate(sampling_rate);
    outRFilter.set_input_sampling_rate(sampling_rate);
    outRFilter.set_output_sampling_rate(sampling_rate);

    middlesidesplitFilter.set_input_sampling_rate(sampling_rate);
    middlesidesplitFilter.set_output_sampling_rate(sampling_rate);
    middlesidemergeFilter.set_input_sampling_rate(sampling_rate);
    middlesidemergeFilter.set_output_sampling_rate(sampling_rate);
    volumemergeFilter.set_input_sampling_rate(sampling_rate);
    volumemergeFilter.set_output_sampling_rate(sampling_rate);
    sumFilter.set_input_sampling_rate(sampling_rate);
    sumFilter.set_output_sampling_rate(sampling_rate);

    powerFilter1.set_input_sampling_rate(sampling_rate);
    powerFilter1.set_output_sampling_rate(sampling_rate);
    attackReleaseFilter1.set_input_sampling_rate(sampling_rate);
    attackReleaseFilter1.set_output_sampling_rate(sampling_rate);
    gainCompressorFilter1.set_input_sampling_rate(sampling_rate);
    gainCompressorFilter1.set_output_sampling_rate(sampling_rate);
    applyGainFilter1.set_input_sampling_rate(sampling_rate);
    applyGainFilter1.set_output_sampling_rate(sampling_rate);
    makeupFilter1.set_input_sampling_rate(sampling_rate);
    makeupFilter1.set_output_sampling_rate(sampling_rate);

    powerFilter2.set_input_sampling_rate(sampling_rate);
    powerFilter2.set_output_sampling_rate(sampling_rate);
    attackReleaseFilter2.set_input_sampling_rate(sampling_rate);
    attackReleaseFilter2.set_output_sampling_rate(sampling_rate);
    gainCompressorFilter2.set_input_sampling_rate(sampling_rate);
    gainCompressorFilter2.set_output_sampling_rate(sampling_rate);
    applyGainFilter2.set_input_sampling_rate(sampling_rate);
    applyGainFilter2.set_output_sampling_rate(sampling_rate);
    makeupFilter2.set_input_sampling_rate(sampling_rate);
    makeupFilter2.set_output_sampling_rate(sampling_rate);
  }

  powerFilter1.set_memory(std::exp(-1 / 1e-3 * GetSampleRate()));
  attackReleaseFilter1.set_release(std::exp(-1 / (GetParam(kAttack1)->Value() * 1e-3 * GetSampleRate()))); // in ms
  attackReleaseFilter1.set_attack(std::exp(-1 / (GetParam(kRelease1)->Value() * 1e-3 * GetSampleRate()))); // in ms
  powerFilter2.set_memory(std::exp(-1 / 1e-3 * GetSampleRate()));
  attackReleaseFilter2.set_release(std::exp(-1 / (GetParam(kAttack2)->Value() * 1e-3 * GetSampleRate()))); // in ms
  attackReleaseFilter2.set_attack(std::exp(-1 / (GetParam(kRelease2)->Value() * 1e-3 * GetSampleRate()))); // in ms
}

void ATKStereoCompressor::OnParamChange(int paramIdx)
{
  IMutexLock lock(this);

  switch (paramIdx)
  {
  case kMiddleside:
    if (GetParam(kMiddleside)->Bool())
    {
    }
    else
    {
    }
    break;
  case kLinkChannels:
    if (GetParam(kLinkChannels)->Bool())
    {
    }
    else
    {
    }
    break;
  case kActivateChannel1:
    if (GetParam(kActivateChannel1)->Bool())
    {
    }
    else
    {
    }
    break;
  case kActivateChannel2:
    if (GetParam(kActivateChannel2)->Bool())
    {
    }
    else
    {
    }
    break;

  case kThreshold1:
    gainCompressorFilter1.set_threshold(std::pow(10, GetParam(kThreshold1)->Value() / 10));
    break;
  case kSlope1:
    gainCompressorFilter1.set_slope(GetParam(kSlope1)->Value());
    break;
  case kSoftness1:
    gainCompressorFilter1.set_softness(std::pow(10, GetParam(kSoftness1)->Value()));
    break;
  case kAttack1:
    attackReleaseFilter1.set_release(std::exp(-1 / (GetParam(kAttack1)->Value() * 1e-3 * GetSampleRate()))); // in ms
    break;
  case kRelease1:
    attackReleaseFilter1.set_attack(std::exp(-1 / (GetParam(kRelease1)->Value() * 1e-3 * GetSampleRate()))); // in ms
    break;
  case kMakeup1:
    makeupFilter1.set_volume_db(GetParam(kMakeup1)->Value());
    break;
  case kThreshold2:
    gainCompressorFilter2.set_threshold(std::pow(10, GetParam(kThreshold2)->Value() / 10));
    break;
  case kSlope2:
    gainCompressorFilter2.set_slope(GetParam(kSlope2)->Value());
    break;
  case kSoftness2:
    gainCompressorFilter2.set_softness(std::pow(10, GetParam(kSoftness2)->Value()));
    break;
  case kAttack2:
    attackReleaseFilter2.set_release(std::exp(-1 / (GetParam(kAttack2)->Value() * 1e-3 * GetSampleRate()))); // in ms
    break;
  case kRelease2:
    attackReleaseFilter2.set_attack(std::exp(-1 / (GetParam(kRelease2)->Value() * 1e-3 * GetSampleRate()))); // in ms
    break;
  case kMakeup2:
    makeupFilter2.set_volume_db(GetParam(kMakeup2)->Value());
    break;

    default:
      break;
  }
}
<commit_msg>Finished the pipeline switches. Need now the UI and tests<commit_after>#include <cmath>

#include "ATKStereoCompressor.h"
#include "IPlug_include_in_plug_src.h"
#include "IControl.h"
#include "resource.h"

const int kNumPrograms = 1;

enum EParams
{
  kMiddleside = 0, // do we process data with middle/side or plain L/R ?
  kLinkChannels,
  kActivateChannel1,
  kActivateChannel2,
  kAttack1,
  kRelease1,
  kThreshold1,
  kSlope1,
  kSoftness1,
  kMakeup1,
  kAttack2,
  kRelease2,
  kThreshold2,
  kSlope2,
  kSoftness2,
  kMakeup2,
  kNumParams
};

enum ELayout
{
  kWidth = GUI_WIDTH,
  kHeight = GUI_HEIGHT,

  kAttackX = 25,
  kAttackY = 31,
  kReleaseX = 94,
  kReleaseY = 31,
  kThresholdX = 163,
  kThresholdY = 31,
  kSlopeX = 232,
  kSlopeY = 31,
  kSoftnessX = 301,
  kSoftnessY = 31,
  kMakeupX = 370,
  kMakeupY = 31,
  kKnobFrames = 43
};

ATKStereoCompressor::ATKStereoCompressor(IPlugInstanceInfo instanceInfo)
  :	IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo),
  inLFilter(NULL, 1, 0, false), inRFilter(NULL, 1, 0, false), volumemergeFilter(2), outLFilter(NULL, 1, 0, false), outRFilter(NULL, 1, 0, false)
{
  TRACE;

  //arguments are: name, defaultVal, minVal, maxVal, step, label
  GetParam(kMiddleside)->InitBool("Middle/Side processing", 0, "");
  GetParam(kLinkChannels)->InitBool("Link channels", 0, "");
  GetParam(kActivateChannel1)->InitBool("Enable Channel 1", 1, "");
  GetParam(kActivateChannel1)->InitBool("Enable Channel 2", 1, "");

  GetParam(kAttack1)->InitDouble("Attack ch1", 10., 1., 100.0, 0.1, "ms");
  GetParam(kAttack1)->SetShape(2.);
  GetParam(kRelease1)->InitDouble("Release ch1", 10, 1., 100.0, 0.1, "ms");
  GetParam(kRelease1)->SetShape(2.);
  GetParam(kThreshold1)->InitDouble("Threshold ch1", 0., -40., 0.0, 0.1, "dB"); // threshold is actually power
  GetParam(kThreshold1)->SetShape(2.);
  GetParam(kSlope1)->InitDouble("Slope ch1", 2., 1, 100, 1, "-");
  GetParam(kSlope1)->SetShape(2.);
  GetParam(kSoftness1)->InitDouble("Softness ch1", -2, -4, 0, 0.1, "-");
  GetParam(kSoftness1)->SetShape(2.);
  GetParam(kMakeup1)->InitDouble("Makeup Gain ch1", 0, 0, 40, 0.1, "-"); // Makeup is expressed in amplitude
  GetParam(kMakeup1)->SetShape(2.);
  GetParam(kAttack2)->InitDouble("Attack ch2", 10., 1., 100.0, 0.1, "ms");
  GetParam(kAttack2)->SetShape(2.);
  GetParam(kRelease2)->InitDouble("Release ch2", 10, 1., 100.0, 0.1, "ms");
  GetParam(kRelease2)->SetShape(2.);
  GetParam(kThreshold2)->InitDouble("Threshold ch2", 0., -40., 0.0, 0.1, "dB"); // threshold is actually power
  GetParam(kThreshold2)->SetShape(2.);
  GetParam(kSlope2)->InitDouble("Slope ch2", 2., 1, 100, 1, "-");
  GetParam(kSlope2)->SetShape(2.);
  GetParam(kSoftness2)->InitDouble("Softness ch2", -2, -4, 0, 0.1, "-");
  GetParam(kSoftness2)->SetShape(2.);
  GetParam(kMakeup2)->InitDouble("Makeup Gain ch2", 0, 0, 40, 0.1, "-"); // Makeup is expressed in amplitude
  GetParam(kMakeup2)->SetShape(2.);

  IGraphics* pGraphics = MakeGraphics(this, kWidth, kHeight);
  pGraphics->AttachBackground(COMPRESSOR_ID, COMPRESSOR_FN);

  IBitmap knob = pGraphics->LoadIBitmap(KNOB_ID, KNOB_FN, kKnobFrames);

  pGraphics->AttachControl(new IKnobMultiControl(this, kAttackX, kAttackY, kAttack1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kReleaseX, kReleaseY, kRelease1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kThresholdX, kThresholdY, kThreshold1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kSlopeX, kSlopeY, kSlope1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kSoftnessX, kSoftnessY, kSoftness1, &knob));
  pGraphics->AttachControl(new IKnobMultiControl(this, kMakeupX, kMakeupY, kMakeup1, &knob));

  AttachGraphics(pGraphics);

  //MakePreset("preset 1", ... );
  MakeDefaultPreset((char *) "-", kNumPrograms);

  volumemergeFilter.set_volume(.5);
  endpoint.add_filter(&outLFilter);
  endpoint.add_filter(&outRFilter);

  powerFilter1.set_input_port(0, &inLFilter, 0);
  gainCompressorFilter1.set_input_port(0, &powerFilter1, 0);
  attackReleaseFilter1.set_input_port(0, &gainCompressorFilter1, 0);
  applyGainFilter1.set_input_port(0, &attackReleaseFilter1, 0);
  applyGainFilter1.set_input_port(1, &inLFilter, 0);
  makeupFilter1.set_input_port(0, &applyGainFilter1, 0);
  outLFilter.set_input_port(0, &makeupFilter1, 0);

  powerFilter2.set_input_port(0, &inRFilter, 0);
  gainCompressorFilter2.set_input_port(0, &powerFilter2, 0);
  attackReleaseFilter2.set_input_port(0, &gainCompressorFilter2, 0);
  applyGainFilter2.set_input_port(0, &attackReleaseFilter2, 0);
  applyGainFilter2.set_input_port(1, &inRFilter, 0);
  makeupFilter2.set_input_port(0, &applyGainFilter2, 0);
  outRFilter.set_input_port(0, &makeupFilter2, 0);

  middlesidesplitFilter.set_input_port(0, &inLFilter, 0);
  middlesidesplitFilter.set_input_port(1, &inRFilter, 0);
  middlesidemergeFilter.set_input_port(0, &makeupFilter1, 0);
  middlesidemergeFilter.set_input_port(1, &makeupFilter2, 0);
  volumemergeFilter.set_input_port(0, &middlesidemergeFilter, 0);
  volumemergeFilter.set_input_port(1, &middlesidemergeFilter, 1);

  Reset();
}

ATKStereoCompressor::~ATKStereoCompressor() {}

void ATKStereoCompressor::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
{
  // Mutex is already locked for us.

  inLFilter.set_pointer(inputs[0], nFrames);
  outLFilter.set_pointer(outputs[0], nFrames);
  inRFilter.set_pointer(inputs[1], nFrames);
  outRFilter.set_pointer(outputs[1], nFrames);
  endpoint.process(nFrames);
}

void ATKStereoCompressor::Reset()
{
  TRACE;
  IMutexLock lock(this);

  int sampling_rate = GetSampleRate();

  if (sampling_rate != endpoint.get_input_sampling_rate())
  {
    inLFilter.set_input_sampling_rate(sampling_rate);
    inLFilter.set_output_sampling_rate(sampling_rate);
    inRFilter.set_input_sampling_rate(sampling_rate);
    inRFilter.set_output_sampling_rate(sampling_rate);
    outLFilter.set_input_sampling_rate(sampling_rate);
    outLFilter.set_output_sampling_rate(sampling_rate);
    outRFilter.set_input_sampling_rate(sampling_rate);
    outRFilter.set_output_sampling_rate(sampling_rate);

    middlesidesplitFilter.set_input_sampling_rate(sampling_rate);
    middlesidesplitFilter.set_output_sampling_rate(sampling_rate);
    middlesidemergeFilter.set_input_sampling_rate(sampling_rate);
    middlesidemergeFilter.set_output_sampling_rate(sampling_rate);
    volumemergeFilter.set_input_sampling_rate(sampling_rate);
    volumemergeFilter.set_output_sampling_rate(sampling_rate);
    sumFilter.set_input_sampling_rate(sampling_rate);
    sumFilter.set_output_sampling_rate(sampling_rate);

    powerFilter1.set_input_sampling_rate(sampling_rate);
    powerFilter1.set_output_sampling_rate(sampling_rate);
    attackReleaseFilter1.set_input_sampling_rate(sampling_rate);
    attackReleaseFilter1.set_output_sampling_rate(sampling_rate);
    gainCompressorFilter1.set_input_sampling_rate(sampling_rate);
    gainCompressorFilter1.set_output_sampling_rate(sampling_rate);
    applyGainFilter1.set_input_sampling_rate(sampling_rate);
    applyGainFilter1.set_output_sampling_rate(sampling_rate);
    makeupFilter1.set_input_sampling_rate(sampling_rate);
    makeupFilter1.set_output_sampling_rate(sampling_rate);

    powerFilter2.set_input_sampling_rate(sampling_rate);
    powerFilter2.set_output_sampling_rate(sampling_rate);
    attackReleaseFilter2.set_input_sampling_rate(sampling_rate);
    attackReleaseFilter2.set_output_sampling_rate(sampling_rate);
    gainCompressorFilter2.set_input_sampling_rate(sampling_rate);
    gainCompressorFilter2.set_output_sampling_rate(sampling_rate);
    applyGainFilter2.set_input_sampling_rate(sampling_rate);
    applyGainFilter2.set_output_sampling_rate(sampling_rate);
    makeupFilter2.set_input_sampling_rate(sampling_rate);
    makeupFilter2.set_output_sampling_rate(sampling_rate);
  }

  powerFilter1.set_memory(std::exp(-1 / 1e-3 * GetSampleRate()));
  attackReleaseFilter1.set_release(std::exp(-1 / (GetParam(kAttack1)->Value() * 1e-3 * GetSampleRate()))); // in ms
  attackReleaseFilter1.set_attack(std::exp(-1 / (GetParam(kRelease1)->Value() * 1e-3 * GetSampleRate()))); // in ms
  powerFilter2.set_memory(std::exp(-1 / 1e-3 * GetSampleRate()));
  attackReleaseFilter2.set_release(std::exp(-1 / (GetParam(kAttack2)->Value() * 1e-3 * GetSampleRate()))); // in ms
  attackReleaseFilter2.set_attack(std::exp(-1 / (GetParam(kRelease2)->Value() * 1e-3 * GetSampleRate()))); // in ms
}

void ATKStereoCompressor::OnParamChange(int paramIdx)
{
  IMutexLock lock(this);

  switch (paramIdx)
  {
  case kMiddleside:
    if (GetParam(kMiddleside)->Bool())
    {
      powerFilter1.set_input_port(0, &middlesidesplitFilter, 0);
      powerFilter2.set_input_port(0, &middlesidesplitFilter, 1);
      outLFilter.set_input_port(0, &volumemergeFilter, 0);
      outRFilter.set_input_port(0, &volumemergeFilter, 1);
    }
    else
    {
      powerFilter1.set_input_port(0, &inLFilter, 0);
      powerFilter2.set_input_port(0, &inRFilter, 1);
      outLFilter.set_input_port(0, &makeupFilter1, 0);
      outRFilter.set_input_port(0, &makeupFilter2, 1);
    }
    break;
  case kLinkChannels:
    if (GetParam(kLinkChannels)->Bool())
    {
      sumFilter.set_input_port(0, &powerFilter1, 0);
      sumFilter.set_input_port(1, &powerFilter2, 0);
      attackReleaseFilter1.set_input_port(0, &sumFilter, 0);
      applyGainFilter2.set_input_port(0, &attackReleaseFilter1, 0);
    }
    else
    {
      attackReleaseFilter1.set_input_port(0, &powerFilter1, 0);
      attackReleaseFilter2.set_input_port(0, &powerFilter2, 0);
      applyGainFilter2.set_input_port(0, &attackReleaseFilter2, 0);
    }
    break;
  case kActivateChannel1:
    if (GetParam(kActivateChannel1)->Bool())
    {
      outLFilter.set_input_port(0, &inLFilter, 0);
    }
    else
    {
      outLFilter.set_input_port(0, &makeupFilter1, 0);
    }
    break;
  case kActivateChannel2:
    if (GetParam(kActivateChannel2)->Bool())
    {
      outRFilter.set_input_port(0, &inRFilter, 0);
    }
    else
    {
      outRFilter.set_input_port(0, &makeupFilter2, 0);
    }
    break;

  case kThreshold1:
    gainCompressorFilter1.set_threshold(std::pow(10, GetParam(kThreshold1)->Value() / 10));
    break;
  case kSlope1:
    gainCompressorFilter1.set_slope(GetParam(kSlope1)->Value());
    break;
  case kSoftness1:
    gainCompressorFilter1.set_softness(std::pow(10, GetParam(kSoftness1)->Value()));
    break;
  case kAttack1:
    attackReleaseFilter1.set_release(std::exp(-1 / (GetParam(kAttack1)->Value() * 1e-3 * GetSampleRate()))); // in ms
    break;
  case kRelease1:
    attackReleaseFilter1.set_attack(std::exp(-1 / (GetParam(kRelease1)->Value() * 1e-3 * GetSampleRate()))); // in ms
    break;
  case kMakeup1:
    makeupFilter1.set_volume_db(GetParam(kMakeup1)->Value());
    break;
  case kThreshold2:
    gainCompressorFilter2.set_threshold(std::pow(10, GetParam(kThreshold2)->Value() / 10));
    break;
  case kSlope2:
    gainCompressorFilter2.set_slope(GetParam(kSlope2)->Value());
    break;
  case kSoftness2:
    gainCompressorFilter2.set_softness(std::pow(10, GetParam(kSoftness2)->Value()));
    break;
  case kAttack2:
    attackReleaseFilter2.set_release(std::exp(-1 / (GetParam(kAttack2)->Value() * 1e-3 * GetSampleRate()))); // in ms
    break;
  case kRelease2:
    attackReleaseFilter2.set_attack(std::exp(-1 / (GetParam(kRelease2)->Value() * 1e-3 * GetSampleRate()))); // in ms
    break;
  case kMakeup2:
    makeupFilter2.set_volume_db(GetParam(kMakeup2)->Value());
    break;

    default:
      break;
  }
}
<|endoftext|>


<commit_before>/***
 * @file mahalanobis_dstance.h
 * @author Ryan Curtin
 *
 * The Mahalanobis distance.
 */
#ifndef __MLPACK_CORE_METRICS_MAHALANOBIS_DISTANCE_HPP
#define __MLPACK_CORE_METRICS_MAHALANOBIS_DISTANCE_HPP

#include <mlpack/core.hpp>

namespace mlpack {
namespace metric {

/**
 * The Mahalanobis distance, which is essentially a stretched Euclidean
 * distance.  Given a square covariance matrix @f$ Q @f$ of size @f$ d @f$ x
 * @f$ d @f$, where @f$ d @f$ is the dimensionality of the points it will be
 * evaluating, and given two vectors @f$ x @f$ and @f$ y @f$ also of
 * dimensionality @f$ d @f$,
 *
 * @f[
 * d(x, y) = \sqrt{(x - y)^T Q (x - y)}
 * @f]
 *
 * where Q is the covariance matrix.
 *
 * Because each evaluation multiplies (x_1 - x_2) by the covariance matrix, it
 * may be much quicker to use an LMetric and simply stretch the actual dataset
 * itself before performing any evaluations.  However, this class is provided
 * for convenience.
 *
 * Similar to the LMetric class, this offers a template parameter t_take_root
 * which, when set to false, will instead evaluate the distance
 *
 * @f[
 * d(x, y) = (x - y)^T Q (x - y)
 * @f]
 *
 * which is faster to evaluate.
 *
 * @tparam t_take_root If true, takes the root of the output.  It is slightly
 *   faster to leave this at the default of false.
 */
template<bool t_take_root = false>
class MahalanobisDistance
{
 public:
  /**
   * Initialize the Mahalanobis distance with the empty matrix as covariance.
   * Because we don't actually know the size of the vectors we will be using, we
   * delay creation of the covariance matrix until evaluation.
   */
  MahalanobisDistance() : covariance(0, 0) { }

  /**
   * Initialize the Mahalanobis distance with the given covariance matrix.  The
   * given covariance matrix will be copied (this is not optimal).
   *
   * @param covariance The covariance matrix to use for this distance.
   */
  MahalanobisDistance(const arma::mat& covariance) : covariance(covariance) { }

  /**
   * Evaluate the distance between the two given points using this Mahalanobis
   * distance.
   *
   * @param a First vector.
   * @param b Second vector.
   */
  template<typename VecType1, typename VecType2>
  double Evaluate(const VecType1& a, const VecType2& b);

  /**
   * Access the covariance matrix.
   *
   * @return Constant reference to the covariance matrix.
   */
  const arma::mat& Covariance() const { return covariance; }

  /**
   * Modify the covariance matrix.
   *
   * @return Reference to the covariance matrix.
   */
  arma::mat& Covariance() { return covariance; }

 private:
  //! The covariance matrix associated with this distance.
  arma::mat covariance;
};

}; // namespace distance
}; // namespace mlpack

#include "mahalanobis_distance_impl.hpp"

#endif
<commit_msg>That empty initalizer is implicit anyway.<commit_after>/***
 * @file mahalanobis_dstance.h
 * @author Ryan Curtin
 *
 * The Mahalanobis distance.
 */
#ifndef __MLPACK_CORE_METRICS_MAHALANOBIS_DISTANCE_HPP
#define __MLPACK_CORE_METRICS_MAHALANOBIS_DISTANCE_HPP

#include <mlpack/core.hpp>

namespace mlpack {
namespace metric {

/**
 * The Mahalanobis distance, which is essentially a stretched Euclidean
 * distance.  Given a square covariance matrix @f$ Q @f$ of size @f$ d @f$ x
 * @f$ d @f$, where @f$ d @f$ is the dimensionality of the points it will be
 * evaluating, and given two vectors @f$ x @f$ and @f$ y @f$ also of
 * dimensionality @f$ d @f$,
 *
 * @f[
 * d(x, y) = \sqrt{(x - y)^T Q (x - y)}
 * @f]
 *
 * where Q is the covariance matrix.
 *
 * Because each evaluation multiplies (x_1 - x_2) by the covariance matrix, it
 * may be much quicker to use an LMetric and simply stretch the actual dataset
 * itself before performing any evaluations.  However, this class is provided
 * for convenience.
 *
 * Similar to the LMetric class, this offers a template parameter t_take_root
 * which, when set to false, will instead evaluate the distance
 *
 * @f[
 * d(x, y) = (x - y)^T Q (x - y)
 * @f]
 *
 * which is faster to evaluate.
 *
 * @tparam t_take_root If true, takes the root of the output.  It is slightly
 *   faster to leave this at the default of false.
 */
template<bool t_take_root = false>
class MahalanobisDistance
{
 public:
  /**
   * Initialize the Mahalanobis distance with the empty matrix as covariance.
   * Because we don't actually know the size of the vectors we will be using, we
   * delay creation of the covariance matrix until evaluation.
   */
  MahalanobisDistance() { }

  /**
   * Initialize the Mahalanobis distance with the given covariance matrix.  The
   * given covariance matrix will be copied (this is not optimal).
   *
   * @param covariance The covariance matrix to use for this distance.
   */
  MahalanobisDistance(const arma::mat& covariance) : covariance(covariance) { }

  /**
   * Evaluate the distance between the two given points using this Mahalanobis
   * distance.
   *
   * @param a First vector.
   * @param b Second vector.
   */
  template<typename VecType1, typename VecType2>
  double Evaluate(const VecType1& a, const VecType2& b);

  /**
   * Access the covariance matrix.
   *
   * @return Constant reference to the covariance matrix.
   */
  const arma::mat& Covariance() const { return covariance; }

  /**
   * Modify the covariance matrix.
   *
   * @return Reference to the covariance matrix.
   */
  arma::mat& Covariance() { return covariance; }

 private:
  //! The covariance matrix associated with this distance.
  arma::mat covariance;
};

}; // namespace distance
}; // namespace mlpack

#include "mahalanobis_distance_impl.hpp"

#endif
<|endoftext|>


<commit_before>/**
 * @file cosine_tree_impl.hpp
 * @author Siddharth Agrawal
 *
 * Implementation of cosine tree.
 */
#include "cosine_tree.hpp"

#include <boost/math/distributions/normal.hpp>

namespace mlpack {
namespace tree {

CosineTree::CosineTree(const arma::mat& dataset) :
    dataset(dataset),
    parent(NULL),
    left(NULL),
    right(NULL),
    numColumns(dataset.n_cols)
{
  // Initialize sizes of column indices and l2 norms.
  indices.resize(numColumns);
  l2NormsSquared.zeros(numColumns);

  // Set indices and calculate squared norms of the columns.
  for(size_t i = 0; i < numColumns; i++)
  {
    indices[i] = i;
    double l2Norm = arma::norm(dataset.col(i), 2);
    l2NormsSquared(i) = l2Norm * l2Norm;
  }

  // Frobenius norm of columns in the node.
  frobNormSquared = arma::accu(l2NormsSquared);

  // Calculate centroid of columns in the node.
  CalculateCentroid();

  splitPointIndex = ColumnSampleLS();
}

CosineTree::CosineTree(CosineTree& parentNode,
                       const std::vector<size_t>& subIndices) :
    dataset(parentNode.GetDataset()),
    parent(&parentNode),
    left(NULL),
    right(NULL),
    numColumns(subIndices.size())
{
  // Initialize sizes of column indices and l2 norms.
  indices.resize(numColumns);
  l2NormsSquared.zeros(numColumns);

  // Set indices and squared norms of the columns.
  for(size_t i = 0; i < numColumns; i++)
  {
    indices[i] = parentNode.indices[subIndices[i]];
    l2NormsSquared(i) = parentNode.l2NormsSquared(subIndices[i]);
  }

  // Frobenius norm of columns in the node.
  frobNormSquared = arma::accu(l2NormsSquared);

  // Calculate centroid of columns in the node.
  CalculateCentroid();

  splitPointIndex = ColumnSampleLS();
}

CosineTree::CosineTree(const arma::mat& dataset,
                       const double epsilon,
                       const double delta) :
    dataset(dataset),
    delta(delta),
    left(NULL),
    right(NULL)
{
  // Declare the cosine tree priority queue.
  CosineNodeQueue treeQueue;

  // Define root node of the tree and add it to the queue.
  CosineTree root(dataset);
  arma::vec tempVector = arma::zeros(dataset.n_rows);
  root.L2Error(0);
  root.BasisVector(tempVector);
  treeQueue.push(&root);

  // Initialize Monte Carlo error estimate for comparison.
  double monteCarloError = root.FrobNormSquared();

  while (monteCarloError > epsilon * root.FrobNormSquared())
  {
    // Pop node from queue with highest projection error.
    CosineTree* currentNode;
    currentNode = treeQueue.top();
    treeQueue.pop();

    // Split the node into left and right children.
    currentNode->CosineNodeSplit();

    // Obtain pointers to the left and right children of the current node.
    CosineTree *currentLeft, *currentRight;
    currentLeft = currentNode->Left();
    currentRight = currentNode->Right();

    // Calculate basis vectors of left and right children.
    arma::vec lBasisVector, rBasisVector;

    ModifiedGramSchmidt(treeQueue, currentLeft->Centroid(), lBasisVector);
    ModifiedGramSchmidt(treeQueue, currentRight->Centroid(), rBasisVector,
                        &lBasisVector);

    // Add basis vectors to their respective nodes.
    currentLeft->BasisVector(lBasisVector);
    currentRight->BasisVector(rBasisVector);

    // Calculate Monte Carlo error estimates for child nodes.
    MonteCarloError(currentLeft, treeQueue, &lBasisVector, &rBasisVector);
    MonteCarloError(currentRight, treeQueue, &lBasisVector, &rBasisVector);

    // Push child nodes into the priority queue.
    treeQueue.push(currentLeft);
    treeQueue.push(currentRight);

    // Calculate Monte Carlo error estimate for the root node.
    monteCarloError = MonteCarloError(&root, treeQueue);
  }

  // Construct the subspace basis from the current priority queue.
  ConstructBasis(treeQueue);
}

CosineTree::~CosineTree()
{
  if (left)
    delete left;
  if (right)
    delete right;
}

void CosineTree::ModifiedGramSchmidt(CosineNodeQueue& treeQueue,
                                     arma::vec& centroid,
                                     arma::vec& newBasisVector,
                                     arma::vec* addBasisVector)
{
  // Set new basis vector to centroid.
  newBasisVector = centroid;

  // Variables for iterating throught the priority queue.
  CosineTree *currentNode;
  CosineNodeQueue::const_iterator i = treeQueue.begin();

  // For every vector in the current basis, remove its projection from the
  // centroid.
  for(; i != treeQueue.end(); i++)
  {
    currentNode = *i;

    double projection = arma::dot(currentNode->BasisVector(), centroid);
    newBasisVector -= projection * currentNode->BasisVector();
  }

  // If additional basis vector is passed, take it into account.
  if(addBasisVector)
  {
    double projection = arma::dot(*addBasisVector, centroid);
    newBasisVector -= *addBasisVector * projection;
  }

  // Normalize the modified centroid vector.
  if(arma::norm(newBasisVector, 2))
    newBasisVector /= arma::norm(newBasisVector, 2);
}

double CosineTree::MonteCarloError(CosineTree* node,
                                   CosineNodeQueue& treeQueue,
                                   arma::vec* addBasisVector1,
                                   arma::vec* addBasisVector2)
{
  std::vector<size_t> sampledIndices;
  arma::vec probabilities;

  // Sample O(log m) points from the input node's distribution.
  // 'm' is the number of columns present in the node.
  size_t numSamples = log(node->NumColumns()) + 1;
  node->ColumnSamplesLS(sampledIndices, probabilities, numSamples);

  // Get pointer to the original dataset.
  arma::mat dataset = node->GetDataset();

  // Initialize weighted projection magnitudes as zeros.
  arma::vec weightedMagnitudes;
  weightedMagnitudes.zeros(numSamples);

  // Set size of projection vector, depending on whether additional basis
  // vectors are passed.
  size_t projectionSize;
  if(addBasisVector1 && addBasisVector2)
    projectionSize = treeQueue.size() + 2;
  else
    projectionSize = treeQueue.size();

  // For each sample, calculate the weighted projection onto the current basis.
  for(size_t i = 0; i < numSamples; i++)
  {
    // Initialize projection as a vector of zeros.
    arma::vec projection;
    projection.zeros(projectionSize);

    CosineTree *currentNode;
    CosineNodeQueue::const_iterator j = treeQueue.begin();

    size_t k = 0;
    // Compute the projection of the sampled vector onto the existing subspace.
    for(; j != treeQueue.end(); j++, k++)
    {
      currentNode = *j;

      projection(k) = arma::dot(dataset.col(sampledIndices[i]),
                                currentNode->BasisVector());
    }
    // If two additional vectors are passed, take their projections.
    if(addBasisVector1 && addBasisVector2)
    {
      projection(k++) = arma::dot(dataset.col(sampledIndices[i]),
                                  *addBasisVector1);
      projection(k) = arma::dot(dataset.col(sampledIndices[i]),
                                *addBasisVector2);
    }

    // Calculate the Frobenius norm squared of the projected vector.
    double frobProjection = arma::norm(projection, "frob");
    double frobProjectionSquared = frobProjection * frobProjection;

    // Calculate the weighted projection magnitude.
    weightedMagnitudes(i) = frobProjectionSquared / probabilities(i);
  }

  // Compute mean and standard deviation of the weighted samples.
  double mu = arma::mean(weightedMagnitudes);
  double sigma = arma::stddev(weightedMagnitudes);

  if(!sigma)
  {
    node->L2Error(node->FrobNormSquared() - mu);
    return (node->FrobNormSquared() - mu);
  }

  // Fit a normal distribution using the calculated statistics, and calculate a
  // lower bound on the magnitudes for the passed 'delta' parameter.
  boost::math::normal dist(mu, sigma);
  double lowerBound = boost::math::quantile(dist, delta);

  // Upper bound on the subspace reconstruction error.
  node->L2Error(node->FrobNormSquared() - lowerBound);

  return (node->FrobNormSquared() - lowerBound);
}

void CosineTree::ConstructBasis(CosineNodeQueue& treeQueue)
{
  // Initialize basis as matrix of zeros.
  basis.zeros(dataset.n_rows, treeQueue.size());

  // Variables for iterating through the priority queue.
  CosineTree *currentNode;
  CosineNodeQueue::const_iterator i = treeQueue.begin();

  // Transfer basis vectors from the queue to the basis matrix.
  size_t j = 0;
  for(; i != treeQueue.end(); i++, j++)
  {
    currentNode = *i;
    basis.col(j) = currentNode->BasisVector();
  }
}

void CosineTree::CosineNodeSplit()
{
  //! If less than two nodes, splitting does not make sense.
  if(numColumns < 3) return;

  //! Calculate cosines with respect to the splitting point.
  arma::vec cosines;
  CalculateCosines(cosines);

  //! Compute maximum and minimum cosine values.
  double cosineMax, cosineMin;
  cosineMax = arma::max(cosines % (cosines < 1));
  cosineMin = arma::min(cosines);

  std::vector<size_t> leftIndices, rightIndices;

  // Split columns into left and right children. The splitting condition for the
  // column to be in the left child is as follows:
  //       cos_max - cos(i) <= cos(i) - cos_min
  for(size_t i = 0; i < numColumns; i++)
  {
    if(cosineMax - cosines(i) <= cosines(i) - cosineMin)
    {
      leftIndices.push_back(i);
    }
    else
    {
      rightIndices.push_back(i);
    }
  }

  // Split the node into left and right children.
  left = new CosineTree(*this, leftIndices);
  right = new CosineTree(*this, rightIndices);
}

void CosineTree::ColumnSamplesLS(std::vector<size_t>& sampledIndices,
                                 arma::vec& probabilities,
                                 size_t numSamples)
{
  // Initialize the cumulative distribution vector size.
  arma::vec cDistribution;
  cDistribution.zeros(numColumns + 1);

  // Calculate cumulative length-squared distribution for the node.
  for(size_t i = 0; i < numColumns; i++)
  {
    cDistribution(i+1) = cDistribution(i) + l2NormsSquared(i) / frobNormSquared;
  }

  // Initialize sizes of the 'sampledIndices' and 'probabilities' vectors.
  sampledIndices.resize(numSamples);
  probabilities.zeros(numSamples);

  for(size_t i = 0; i < numSamples; i++)
  {
    // Generate a random value for sampling.
    double randValue = arma::randu();
    size_t start = 0, end = numColumns, searchIndex;

    // Sample from the distribution and store corresponding probability.
    searchIndex = BinarySearch(cDistribution, randValue, start, end);
    sampledIndices[i] = indices[searchIndex];
    probabilities(i) = l2NormsSquared(searchIndex) / frobNormSquared;
  }
}

size_t CosineTree::ColumnSampleLS()
{
  // If only one element is present, there can only be one sample.
  if(numColumns < 2)
  {
    return 0;
  }

  // Initialize the cumulative distribution vector size.
  arma::vec cDistribution;
  cDistribution.zeros(numColumns + 1);

  // Calculate cumulative length-squared distribution for the node.
  for(size_t i = 0; i < numColumns; i++)
  {
    cDistribution(i+1) = cDistribution(i) + l2NormsSquared(i) / frobNormSquared;
  }

  // Generate a random value for sampling.
  double randValue = arma::randu();
  size_t start = 0, end = numColumns;

  // Sample from the distribution.
  return BinarySearch(cDistribution, randValue, start, end);
}

size_t CosineTree::BinarySearch(arma::vec& cDistribution,
                                double value,
                                size_t start,
                                size_t end)
{
  size_t pivot = (start + end) / 2;

  // If pivot is zero, first point is the sampled point.
  if(!pivot)
  {
    return pivot;
  }

  // Binary search recursive algorithm.
  if(value > cDistribution(pivot - 1) && value <= cDistribution(pivot))
  {
    return (pivot - 1);
  }
  else if(value < cDistribution(pivot - 1))
  {
    return BinarySearch(cDistribution, value, start, pivot - 1);
  }
  else
  {
    return BinarySearch(cDistribution, value, pivot + 1, end);
  }
}

void CosineTree::CalculateCosines(arma::vec& cosines)
{
  // Initialize cosine vector as a vector of zeros.
  cosines.zeros(numColumns);

  for(size_t i = 0; i < numColumns; i++)
  {
    // If norm is zero, store cosine value as zero. Else, calculate cosine value
    // between two vectors.
    if(l2NormsSquared(i) == 0)
    {
      cosines(i) = 0;
    }
    else
    {
      cosines(i) = arma::norm_dot(dataset.col(indices[splitPointIndex]),
                                  dataset.col(indices[i]));
    }
  }
}

void CosineTree::CalculateCentroid()
{
  // Initialize centroid as vector of zeros.
  centroid.zeros(dataset.n_rows);

  // Calculate centroid of columns in the node.
  for(size_t i = 0; i < numColumns; i++)
  {
    centroid += dataset.col(indices[i]);
  }
  centroid /= numColumns;
}

} // namespace tree
} // namespace mlpack
<commit_msg>Fix spacing.<commit_after>/**
 * @file cosine_tree_impl.hpp
 * @author Siddharth Agrawal
 *
 * Implementation of cosine tree.
 */
#include "cosine_tree.hpp"

#include <boost/math/distributions/normal.hpp>

namespace mlpack {
namespace tree {

CosineTree::CosineTree(const arma::mat& dataset) :
    dataset(dataset),
    parent(NULL),
    left(NULL),
    right(NULL),
    numColumns(dataset.n_cols)
{
  // Initialize sizes of column indices and l2 norms.
  indices.resize(numColumns);
  l2NormsSquared.zeros(numColumns);

  // Set indices and calculate squared norms of the columns.
  for (size_t i = 0; i < numColumns; i++)
  {
    indices[i] = i;
    double l2Norm = arma::norm(dataset.col(i), 2);
    l2NormsSquared(i) = l2Norm * l2Norm;
  }

  // Frobenius norm of columns in the node.
  frobNormSquared = arma::accu(l2NormsSquared);

  // Calculate centroid of columns in the node.
  CalculateCentroid();

  splitPointIndex = ColumnSampleLS();
}

CosineTree::CosineTree(CosineTree& parentNode,
                       const std::vector<size_t>& subIndices) :
    dataset(parentNode.GetDataset()),
    parent(&parentNode),
    left(NULL),
    right(NULL),
    numColumns(subIndices.size())
{
  // Initialize sizes of column indices and l2 norms.
  indices.resize(numColumns);
  l2NormsSquared.zeros(numColumns);

  // Set indices and squared norms of the columns.
  for (size_t i = 0; i < numColumns; i++)
  {
    indices[i] = parentNode.indices[subIndices[i]];
    l2NormsSquared(i) = parentNode.l2NormsSquared(subIndices[i]);
  }

  // Frobenius norm of columns in the node.
  frobNormSquared = arma::accu(l2NormsSquared);

  // Calculate centroid of columns in the node.
  CalculateCentroid();

  splitPointIndex = ColumnSampleLS();
}

CosineTree::CosineTree(const arma::mat& dataset,
                       const double epsilon,
                       const double delta) :
    dataset(dataset),
    delta(delta),
    left(NULL),
    right(NULL)
{
  // Declare the cosine tree priority queue.
  CosineNodeQueue treeQueue;

  // Define root node of the tree and add it to the queue.
  CosineTree root(dataset);
  arma::vec tempVector = arma::zeros(dataset.n_rows);
  root.L2Error(0);
  root.BasisVector(tempVector);
  treeQueue.push(&root);

  // Initialize Monte Carlo error estimate for comparison.
  double monteCarloError = root.FrobNormSquared();

  while (monteCarloError > epsilon * root.FrobNormSquared())
  {
    // Pop node from queue with highest projection error.
    CosineTree* currentNode;
    currentNode = treeQueue.top();
    treeQueue.pop();

    // Split the node into left and right children.
    currentNode->CosineNodeSplit();

    // Obtain pointers to the left and right children of the current node.
    CosineTree *currentLeft, *currentRight;
    currentLeft = currentNode->Left();
    currentRight = currentNode->Right();

    // Calculate basis vectors of left and right children.
    arma::vec lBasisVector, rBasisVector;

    ModifiedGramSchmidt(treeQueue, currentLeft->Centroid(), lBasisVector);
    ModifiedGramSchmidt(treeQueue, currentRight->Centroid(), rBasisVector,
                        &lBasisVector);

    // Add basis vectors to their respective nodes.
    currentLeft->BasisVector(lBasisVector);
    currentRight->BasisVector(rBasisVector);

    // Calculate Monte Carlo error estimates for child nodes.
    MonteCarloError(currentLeft, treeQueue, &lBasisVector, &rBasisVector);
    MonteCarloError(currentRight, treeQueue, &lBasisVector, &rBasisVector);

    // Push child nodes into the priority queue.
    treeQueue.push(currentLeft);
    treeQueue.push(currentRight);

    // Calculate Monte Carlo error estimate for the root node.
    monteCarloError = MonteCarloError(&root, treeQueue);
  }

  // Construct the subspace basis from the current priority queue.
  ConstructBasis(treeQueue);
}

CosineTree::~CosineTree()
{
  if (left)
    delete left;
  if (right)
    delete right;
}

void CosineTree::ModifiedGramSchmidt(CosineNodeQueue& treeQueue,
                                     arma::vec& centroid,
                                     arma::vec& newBasisVector,
                                     arma::vec* addBasisVector)
{
  // Set new basis vector to centroid.
  newBasisVector = centroid;

  // Variables for iterating throught the priority queue.
  CosineTree *currentNode;
  CosineNodeQueue::const_iterator i = treeQueue.begin();

  // For every vector in the current basis, remove its projection from the
  // centroid.
  for ( ; i != treeQueue.end(); i++)
  {
    currentNode = *i;

    double projection = arma::dot(currentNode->BasisVector(), centroid);
    newBasisVector -= projection * currentNode->BasisVector();
  }

  // If additional basis vector is passed, take it into account.
  if (addBasisVector)
  {
    double projection = arma::dot(*addBasisVector, centroid);
    newBasisVector -= *addBasisVector * projection;
  }

  // Normalize the modified centroid vector.
  if (arma::norm(newBasisVector, 2))
    newBasisVector /= arma::norm(newBasisVector, 2);
}

double CosineTree::MonteCarloError(CosineTree* node,
                                   CosineNodeQueue& treeQueue,
                                   arma::vec* addBasisVector1,
                                   arma::vec* addBasisVector2)
{
  std::vector<size_t> sampledIndices;
  arma::vec probabilities;

  // Sample O(log m) points from the input node's distribution.
  // 'm' is the number of columns present in the node.
  size_t numSamples = log(node->NumColumns()) + 1;
  node->ColumnSamplesLS(sampledIndices, probabilities, numSamples);

  // Get pointer to the original dataset.
  arma::mat dataset = node->GetDataset();

  // Initialize weighted projection magnitudes as zeros.
  arma::vec weightedMagnitudes;
  weightedMagnitudes.zeros(numSamples);

  // Set size of projection vector, depending on whether additional basis
  // vectors are passed.
  size_t projectionSize;
  if (addBasisVector1 && addBasisVector2)
    projectionSize = treeQueue.size() + 2;
  else
    projectionSize = treeQueue.size();

  // For each sample, calculate the weighted projection onto the current basis.
  for (size_t i = 0; i < numSamples; i++)
  {
    // Initialize projection as a vector of zeros.
    arma::vec projection;
    projection.zeros(projectionSize);

    CosineTree *currentNode;
    CosineNodeQueue::const_iterator j = treeQueue.begin();

    size_t k = 0;
    // Compute the projection of the sampled vector onto the existing subspace.
    for ( ; j != treeQueue.end(); j++, k++)
    {
      currentNode = *j;

      projection(k) = arma::dot(dataset.col(sampledIndices[i]),
                                currentNode->BasisVector());
    }
    // If two additional vectors are passed, take their projections.
    if (addBasisVector1 && addBasisVector2)
    {
      projection(k++) = arma::dot(dataset.col(sampledIndices[i]),
                                  *addBasisVector1);
      projection(k) = arma::dot(dataset.col(sampledIndices[i]),
                                *addBasisVector2);
    }

    // Calculate the Frobenius norm squared of the projected vector.
    double frobProjection = arma::norm(projection, "frob");
    double frobProjectionSquared = frobProjection * frobProjection;

    // Calculate the weighted projection magnitude.
    weightedMagnitudes(i) = frobProjectionSquared / probabilities(i);
  }

  // Compute mean and standard deviation of the weighted samples.
  double mu = arma::mean(weightedMagnitudes);
  double sigma = arma::stddev(weightedMagnitudes);

  if (!sigma)
  {
    node->L2Error(node->FrobNormSquared() - mu);
    return (node->FrobNormSquared() - mu);
  }

  // Fit a normal distribution using the calculated statistics, and calculate a
  // lower bound on the magnitudes for the passed 'delta' parameter.
  boost::math::normal dist(mu, sigma);
  double lowerBound = boost::math::quantile(dist, delta);

  // Upper bound on the subspace reconstruction error.
  node->L2Error(node->FrobNormSquared() - lowerBound);

  return (node->FrobNormSquared() - lowerBound);
}

void CosineTree::ConstructBasis(CosineNodeQueue& treeQueue)
{
  // Initialize basis as matrix of zeros.
  basis.zeros(dataset.n_rows, treeQueue.size());

  // Variables for iterating through the priority queue.
  CosineTree *currentNode;
  CosineNodeQueue::const_iterator i = treeQueue.begin();

  // Transfer basis vectors from the queue to the basis matrix.
  size_t j = 0;
  for ( ; i != treeQueue.end(); i++, j++)
  {
    currentNode = *i;
    basis.col(j) = currentNode->BasisVector();
  }
}

void CosineTree::CosineNodeSplit()
{
  //! If less than two nodes, splitting does not make sense.
  if (numColumns < 3) return;

  //! Calculate cosines with respect to the splitting point.
  arma::vec cosines;
  CalculateCosines(cosines);

  //! Compute maximum and minimum cosine values.
  double cosineMax, cosineMin;
  cosineMax = arma::max(cosines % (cosines < 1));
  cosineMin = arma::min(cosines);

  std::vector<size_t> leftIndices, rightIndices;

  // Split columns into left and right children. The splitting condition for the
  // column to be in the left child is as follows:
  //       cos_max - cos(i) <= cos(i) - cos_min
  for (size_t i = 0; i < numColumns; i++)
  {
    if (cosineMax - cosines(i) <= cosines(i) - cosineMin)
    {
      leftIndices.push_back(i);
    }
    else
    {
      rightIndices.push_back(i);
    }
  }

  // Split the node into left and right children.
  left = new CosineTree(*this, leftIndices);
  right = new CosineTree(*this, rightIndices);
}

void CosineTree::ColumnSamplesLS(std::vector<size_t>& sampledIndices,
                                 arma::vec& probabilities,
                                 size_t numSamples)
{
  // Initialize the cumulative distribution vector size.
  arma::vec cDistribution;
  cDistribution.zeros(numColumns + 1);

  // Calculate cumulative length-squared distribution for the node.
  for (size_t i = 0; i < numColumns; i++)
  {
    cDistribution(i + 1) = cDistribution(i) +
        (l2NormsSquared(i) / frobNormSquared);
  }

  // Initialize sizes of the 'sampledIndices' and 'probabilities' vectors.
  sampledIndices.resize(numSamples);
  probabilities.zeros(numSamples);

  for (size_t i = 0; i < numSamples; i++)
  {
    // Generate a random value for sampling.
    double randValue = arma::randu();
    size_t start = 0, end = numColumns, searchIndex;

    // Sample from the distribution and store corresponding probability.
    searchIndex = BinarySearch(cDistribution, randValue, start, end);
    sampledIndices[i] = indices[searchIndex];
    probabilities(i) = l2NormsSquared(searchIndex) / frobNormSquared;
  }
}

size_t CosineTree::ColumnSampleLS()
{
  // If only one element is present, there can only be one sample.
  if (numColumns < 2)
  {
    return 0;
  }

  // Initialize the cumulative distribution vector size.
  arma::vec cDistribution;
  cDistribution.zeros(numColumns + 1);

  // Calculate cumulative length-squared distribution for the node.
  for (size_t i = 0; i < numColumns; i++)
  {
    cDistribution(i + 1) = cDistribution(i) +
        (l2NormsSquared(i) / frobNormSquared);
  }

  // Generate a random value for sampling.
  double randValue = arma::randu();
  size_t start = 0, end = numColumns;

  // Sample from the distribution.
  return BinarySearch(cDistribution, randValue, start, end);
}

size_t CosineTree::BinarySearch(arma::vec& cDistribution,
                                double value,
                                size_t start,
                                size_t end)
{
  size_t pivot = (start + end) / 2;

  // If pivot is zero, first point is the sampled point.
  if (!pivot)
  {
    return pivot;
  }

  // Binary search recursive algorithm.
  if (value > cDistribution(pivot - 1) && value <= cDistribution(pivot))
  {
    return (pivot - 1);
  }
  else if (value < cDistribution(pivot - 1))
  {
    return BinarySearch(cDistribution, value, start, pivot - 1);
  }
  else
  {
    return BinarySearch(cDistribution, value, pivot + 1, end);
  }
}

void CosineTree::CalculateCosines(arma::vec& cosines)
{
  // Initialize cosine vector as a vector of zeros.
  cosines.zeros(numColumns);

  for (size_t i = 0; i < numColumns; i++)
  {
    // If norm is zero, store cosine value as zero. Else, calculate cosine value
    // between two vectors.
    if (l2NormsSquared(i) == 0)
    {
      cosines(i) = 0;
    }
    else
    {
      cosines(i) = arma::norm_dot(dataset.col(indices[splitPointIndex]),
                                  dataset.col(indices[i]));
    }
  }
}

void CosineTree::CalculateCentroid()
{
  // Initialize centroid as vector of zeros.
  centroid.zeros(dataset.n_rows);

  // Calculate centroid of columns in the node.
  for (size_t i = 0; i < numColumns; i++)
  {
    centroid += dataset.col(indices[i]);
  }
  centroid /= numColumns;
}

} // namespace tree
} // namespace mlpack
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2011 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 "video_coding.h"
#include "trace.h"
#include "encoded_frame.h"
#include "internal_defines.h"
#include "receiver.h"
#include "tick_time.h"

#include <assert.h>

namespace webrtc {

VCMReceiver::VCMReceiver(VCMTiming& timing,
                         WebRtc_Word32 vcmId,
                         WebRtc_Word32 receiverId,
                         bool master)
:
_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
_vcmId(vcmId),
_receiverId(receiverId),
_master(master),
_jitterBuffer(vcmId, receiverId, master),
_timing(timing),
_renderWaitEvent(*new VCMEvent()),
_state(kPassive)
{
}

VCMReceiver::~VCMReceiver()
{
    _renderWaitEvent.Set();
    delete &_renderWaitEvent;
    delete &_critSect;
}

WebRtc_Word32
VCMReceiver::Initialize()
{
    CriticalSectionScoped cs(_critSect);
    if (!_jitterBuffer.Running())
    {
        _jitterBuffer.Start();
    }
    else
    {
        _jitterBuffer.Flush();
    }
    _renderWaitEvent.Reset();
    if (_master)
    {
        _state = kReceiving;
    }
    else
    {
        _state = kPassive;
        SetNackMode(kNoNack);
    }
    return VCM_OK;
}

void VCMReceiver::UpdateRtt(WebRtc_UWord32 rtt)
{
    _jitterBuffer.UpdateRtt(rtt);
}

WebRtc_Word32
VCMReceiver::InsertPacket(const VCMPacket& packet,
                          WebRtc_UWord16 frameWidth,
                          WebRtc_UWord16 frameHeight)
{
    // Find an empty frame
    VCMEncodedFrame *buffer = NULL;
    const WebRtc_Word32 error = _jitterBuffer.GetFrame(packet, buffer);
    if (error == VCM_OLD_PACKET_ERROR)
    {
        return VCM_OK;
    }
    else if (error < 0)
    {
        return error;
    }

    {
        CriticalSectionScoped cs(_critSect);

        if (frameWidth && frameHeight)
        {
            buffer->SetEncodedSize(static_cast<WebRtc_UWord32>(frameWidth),
                                   static_cast<WebRtc_UWord32>(frameHeight));
        }

        if (_master)
        {
            // Only trace the primary receiver to make it possible
            // to parse and plot the trace file.
            WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                       "Packet seqNo %u of frame %u at %u",
                       packet.seqNum, packet.timestamp,
                       MaskWord64ToUWord32(VCMTickTime::MillisecondTimestamp()));
        }

        const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();

        WebRtc_Word64 renderTimeMs = _timing.RenderTimeMs(packet.timestamp, nowMs);

        if (renderTimeMs < 0)
        {
            // Render time error. Assume that this is due to some change in
            // the incoming video stream and reset the JB and the timing.
            _jitterBuffer.Flush();
            _timing.Reset();
            return VCM_OK;
        }
        else if (renderTimeMs < nowMs - kMaxVideoDelayMs)
        {
            WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                "This frame should have been rendered more than %u ms ago."
                "Flushing jitter buffer and resetting timing.", kMaxVideoDelayMs);
            _jitterBuffer.Flush();
            _timing.Reset();
            return VCM_OK;
        }
        else if (_timing.TargetVideoDelay() > kMaxVideoDelayMs)
        {
            WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                "More than %u ms target delay. Flushing jitter buffer and resetting timing.",
                kMaxVideoDelayMs);
            _jitterBuffer.Flush();
            _timing.Reset();
            return VCM_OK;
        }

        // First packet received belonging to this frame.
        if (buffer->Length() == 0)
        {
            const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
            if (_master)
            {
                // Only trace the primary receiver to make it possible to parse and plot the trace file.
                WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                           "First packet of frame %u at %u", packet.timestamp,
                           MaskWord64ToUWord32(nowMs));
            }
            renderTimeMs = _timing.RenderTimeMs(packet.timestamp, nowMs);
            if (renderTimeMs >= 0)
            {
                buffer->SetRenderTime(renderTimeMs);
            }
            else
            {
                buffer->SetRenderTime(nowMs);
            }
        }

        // Insert packet into jitter buffer
        // both media and empty packets
        const VCMFrameBufferEnum ret = _jitterBuffer.InsertPacket(buffer, packet);

        if (ret < 0)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                       "Error inserting packet seqNo=%u, timeStamp=%u",
                       packet.seqNum, packet.timestamp);
            return VCM_JITTER_BUFFER_ERROR;
        }
    }
    return VCM_OK;
}

VCMEncodedFrame*
VCMReceiver::FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs, WebRtc_Word64& nextRenderTimeMs,
                              bool renderTiming, VCMReceiver* dualReceiver)
{
    // No need to enter the critical section here since the jitter buffer
    // is thread-safe.
    FrameType incomingFrameType = kVideoFrameDelta;
    nextRenderTimeMs = -1;
    const WebRtc_Word64 startTimeMs = VCMTickTime::MillisecondTimestamp();
    WebRtc_Word64 ret = _jitterBuffer.GetNextTimeStamp(maxWaitTimeMs,
                                                       incomingFrameType,
                                                       nextRenderTimeMs);
    if (ret < 0)
    {
        // No timestamp in jitter buffer at the moment
        return NULL;
    }
    const WebRtc_UWord32 timeStamp = static_cast<WebRtc_UWord32>(ret);

    // Update the timing
    _timing.SetRequiredDelay(_jitterBuffer.GetEstimatedJitterMS());
    _timing.UpdateCurrentDelay(timeStamp);

    const WebRtc_Word32 tempWaitTime = maxWaitTimeMs -
            static_cast<WebRtc_Word32>(VCMTickTime::MillisecondTimestamp() - startTimeMs);
    WebRtc_UWord16 newMaxWaitTime = static_cast<WebRtc_UWord16>(VCM_MAX(tempWaitTime, 0));

    VCMEncodedFrame* frame = NULL;

    if (renderTiming)
    {
        frame = FrameForDecoding(newMaxWaitTime, nextRenderTimeMs, dualReceiver);
    }
    else
    {
        frame = FrameForRendering(newMaxWaitTime, nextRenderTimeMs, dualReceiver);
    }

    if (frame != NULL)
    {
        bool retransmitted = false;
        const WebRtc_Word64 lastPacketTimeMs =
                _jitterBuffer.LastPacketTime(frame, retransmitted);
        if (lastPacketTimeMs >= 0 && !retransmitted)
        {
            // We don't want to include timestamps which have suffered from retransmission
            // here, since we compensate with extra retransmission delay within
            // the jitter estimate.
            _timing.IncomingTimestamp(timeStamp, lastPacketTimeMs);
        }
        if (dualReceiver != NULL)
        {
            dualReceiver->UpdateState(*frame);
        }
    }
    return frame;
}

VCMEncodedFrame*
VCMReceiver::FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs,
                              WebRtc_Word64 nextRenderTimeMs,
                              VCMReceiver* dualReceiver)
{
    // How long can we wait until we must decode the next frame
    WebRtc_UWord32 waitTimeMs = _timing.MaxWaitingTime(nextRenderTimeMs,
                                          VCMTickTime::MillisecondTimestamp());

    // Try to get a complete frame from the jitter buffer
    VCMEncodedFrame* frame = _jitterBuffer.GetCompleteFrameForDecoding(0);

    if (frame == NULL && maxWaitTimeMs == 0 && waitTimeMs > 0)
    {
        // If we're not allowed to wait for frames to get complete we must
        // calculate if it's time to decode, and if it's not we will just return
        // for now.
        return NULL;
    }

    if (frame == NULL && VCM_MIN(waitTimeMs, maxWaitTimeMs) == 0)
    {
        // No time to wait for a complete frame,
        // check if we have an incomplete
        frame = _jitterBuffer.GetFrameForDecoding();
    }
    if (frame == NULL)
    {
        // Wait for a complete frame
        frame = _jitterBuffer.GetCompleteFrameForDecoding(maxWaitTimeMs);
    }
    if (frame == NULL)
    {
        // Get an incomplete frame
        if (_timing.MaxWaitingTime(nextRenderTimeMs,
                                   VCMTickTime::MillisecondTimestamp()) > 0)
        {
            // Still time to wait for a complete frame
            return NULL;
        }

        // No time left to wait, we must decode this frame now.
        const bool dualReceiverEnabledAndPassive = (dualReceiver != NULL &&
                                     dualReceiver->State() == kPassive &&
                                     dualReceiver->NackMode() == kNackInfinite);
        if (dualReceiverEnabledAndPassive &&
            !_jitterBuffer.CompleteSequenceWithNextFrame())
        {
            // Jitter buffer state might get corrupt with this frame.
            dualReceiver->CopyJitterBufferStateFromReceiver(*this);
        }

        frame = _jitterBuffer.GetFrameForDecoding();
    }
    return frame;
}

VCMEncodedFrame*
VCMReceiver::FrameForRendering(WebRtc_UWord16 maxWaitTimeMs,
                               WebRtc_Word64 nextRenderTimeMs,
                               VCMReceiver* dualReceiver)
{
    // How long MUST we wait until we must decode the next frame. This is different for the case
    // where we have a renderer which can render at a specified time. Here we must wait as long
    // as possible before giving the frame to the decoder, which will render the frame as soon
    // as it has been decoded.
    WebRtc_UWord32 waitTimeMs = _timing.MaxWaitingTime(nextRenderTimeMs,
                                                       VCMTickTime::MillisecondTimestamp());
    if (maxWaitTimeMs < waitTimeMs)
    {
        // If we're not allowed to wait until the frame is supposed to be rendered
        // we will have to return NULL for now.
        return NULL;
    }
    // Wait until it's time to render
    _renderWaitEvent.Wait(waitTimeMs);

    // Get a complete frame if possible
    VCMEncodedFrame* frame = _jitterBuffer.GetCompleteFrameForDecoding(0);

    if (frame == NULL)
    {
        // Get an incomplete frame
        const bool dualReceiverEnabledAndPassive = dualReceiver != NULL &&
                                                   dualReceiver->State() == kPassive &&
                                                   dualReceiver->NackMode() == kNackInfinite;
        if (dualReceiverEnabledAndPassive && !_jitterBuffer.CompleteSequenceWithNextFrame())
        {
            // Jitter buffer state might get corrupt with this frame.
            dualReceiver->CopyJitterBufferStateFromReceiver(*this);
        }

        frame = _jitterBuffer.GetFrameForDecoding();
    }
    return frame;
}

void
VCMReceiver::ReleaseFrame(VCMEncodedFrame* frame)
{
    _jitterBuffer.ReleaseFrame(frame);
}

WebRtc_Word32
VCMReceiver::ReceiveStatistics(WebRtc_UWord32& bitRate, WebRtc_UWord32& frameRate)
{
    const WebRtc_Word32 ret = _jitterBuffer.GetUpdate(frameRate, bitRate);
    bitRate /= 1000; // Should be in kbps
    return ret;
}

WebRtc_Word32
VCMReceiver::ReceivedFrameCount(VCMFrameCount& frameCount) const
{
    return _jitterBuffer.GetFrameStatistics(frameCount.numDeltaFrames,
                                            frameCount.numKeyFrames);
}

WebRtc_UWord32 VCMReceiver::DiscardedPackets() const {
  return _jitterBuffer.DiscardedPackets();
}

void
VCMReceiver::SetNackMode(VCMNackMode nackMode)
{
    CriticalSectionScoped cs(_critSect);
    _jitterBuffer.SetNackMode(nackMode);
    if (!_master)
    {
        _state = kPassive; // The dual decoder defaults to passive
    }
}

VCMNackMode
VCMReceiver::NackMode() const
{
    CriticalSectionScoped cs(_critSect);
    return _jitterBuffer.GetNackMode();
}

VCMNackStatus
VCMReceiver::NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size)
{
    bool extended = false;
    WebRtc_UWord16 nackListSize = 0;
    WebRtc_UWord16* internalNackList = _jitterBuffer.GetNackList(nackListSize, extended);
    if (internalNackList == NULL && nackListSize == 0xffff)
    {
        // This combination is used to trigger key frame requests.
        size = 0;
        return kNackKeyFrameRequest;
    }
    if (nackListSize > size)
    {
        size = nackListSize;
        return kNackNeedMoreMemory;
    }
    memcpy(nackList, internalNackList, nackListSize * sizeof(WebRtc_UWord16));
    size = nackListSize;
    return kNackOk;
}

// Decide whether we should change decoder state. This should be done if the dual decoder
// has caught up with the decoder decoding with packet losses.
bool
VCMReceiver::DualDecoderCaughtUp(VCMEncodedFrame* dualFrame, VCMReceiver& dualReceiver) const
{
    if (dualFrame == NULL)
    {
        return false;
    }
    if (_jitterBuffer.LastDecodedTimestamp() == dualFrame->TimeStamp())
    {
        dualReceiver.UpdateState(kWaitForPrimaryDecode);
        return true;
    }
    return false;
}

void
VCMReceiver::CopyJitterBufferStateFromReceiver(const VCMReceiver& receiver)
{
    _jitterBuffer = receiver._jitterBuffer;
}

VCMReceiverState
VCMReceiver::State() const
{
    CriticalSectionScoped cs(_critSect);
    return _state;
}

void
VCMReceiver::UpdateState(VCMReceiverState newState)
{
    CriticalSectionScoped cs(_critSect);
    assert(!(_state == kPassive && newState == kWaitForPrimaryDecode));
//    assert(!(_state == kReceiving && newState == kPassive));
    _state = newState;
}

void
VCMReceiver::UpdateState(VCMEncodedFrame& frame)
{
    if (_jitterBuffer.GetNackMode() == kNoNack)
    {
        // Dual decoder mode has not been enabled.
        return;
    }
    // Update the dual receiver state
    if (frame.Complete() && frame.FrameType() == kVideoFrameKey)
    {
        UpdateState(kPassive);
    }
    if (State() == kWaitForPrimaryDecode &&
        frame.Complete() && !frame.MissingFrame())
    {
        UpdateState(kPassive);
    }
    if (frame.MissingFrame() || !frame.Complete())
    {
        // State was corrupted, enable dual receiver.
        UpdateState(kReceiving);
    }
}

}
<commit_msg>Workaround for an issue where frames are grabbed for decoding prematurely.<commit_after>/*
 *  Copyright (c) 2011 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 "video_coding.h"
#include "trace.h"
#include "encoded_frame.h"
#include "internal_defines.h"
#include "receiver.h"
#include "tick_time.h"

#include <assert.h>

namespace webrtc {

VCMReceiver::VCMReceiver(VCMTiming& timing,
                         WebRtc_Word32 vcmId,
                         WebRtc_Word32 receiverId,
                         bool master)
:
_critSect(*CriticalSectionWrapper::CreateCriticalSection()),
_vcmId(vcmId),
_receiverId(receiverId),
_master(master),
_jitterBuffer(vcmId, receiverId, master),
_timing(timing),
_renderWaitEvent(*new VCMEvent()),
_state(kPassive)
{
}

VCMReceiver::~VCMReceiver()
{
    _renderWaitEvent.Set();
    delete &_renderWaitEvent;
    delete &_critSect;
}

WebRtc_Word32
VCMReceiver::Initialize()
{
    CriticalSectionScoped cs(_critSect);
    if (!_jitterBuffer.Running())
    {
        _jitterBuffer.Start();
    }
    else
    {
        _jitterBuffer.Flush();
    }
    _renderWaitEvent.Reset();
    if (_master)
    {
        _state = kReceiving;
    }
    else
    {
        _state = kPassive;
        SetNackMode(kNoNack);
    }
    return VCM_OK;
}

void VCMReceiver::UpdateRtt(WebRtc_UWord32 rtt)
{
    _jitterBuffer.UpdateRtt(rtt);
}

WebRtc_Word32
VCMReceiver::InsertPacket(const VCMPacket& packet,
                          WebRtc_UWord16 frameWidth,
                          WebRtc_UWord16 frameHeight)
{
    // Find an empty frame
    VCMEncodedFrame *buffer = NULL;
    const WebRtc_Word32 error = _jitterBuffer.GetFrame(packet, buffer);
    if (error == VCM_OLD_PACKET_ERROR)
    {
        return VCM_OK;
    }
    else if (error < 0)
    {
        return error;
    }

    {
        CriticalSectionScoped cs(_critSect);

        if (frameWidth && frameHeight)
        {
            buffer->SetEncodedSize(static_cast<WebRtc_UWord32>(frameWidth),
                                   static_cast<WebRtc_UWord32>(frameHeight));
        }

        if (_master)
        {
            // Only trace the primary receiver to make it possible
            // to parse and plot the trace file.
            WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                       "Packet seqNo %u of frame %u at %u",
                       packet.seqNum, packet.timestamp,
                       MaskWord64ToUWord32(VCMTickTime::MillisecondTimestamp()));
        }

        const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();

        WebRtc_Word64 renderTimeMs = _timing.RenderTimeMs(packet.timestamp, nowMs);

        if (renderTimeMs < 0)
        {
            // Render time error. Assume that this is due to some change in
            // the incoming video stream and reset the JB and the timing.
            _jitterBuffer.Flush();
            _timing.Reset();
            return VCM_OK;
        }
        else if (renderTimeMs < nowMs - kMaxVideoDelayMs)
        {
            WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                "This frame should have been rendered more than %u ms ago."
                "Flushing jitter buffer and resetting timing.", kMaxVideoDelayMs);
            _jitterBuffer.Flush();
            _timing.Reset();
            return VCM_OK;
        }
        else if (_timing.TargetVideoDelay() > kMaxVideoDelayMs)
        {
            WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                "More than %u ms target delay. Flushing jitter buffer and resetting timing.",
                kMaxVideoDelayMs);
            _jitterBuffer.Flush();
            _timing.Reset();
            return VCM_OK;
        }

        // First packet received belonging to this frame.
        if (buffer->Length() == 0)
        {
            const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp();
            if (_master)
            {
                // Only trace the primary receiver to make it possible to parse and plot the trace file.
                WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                           "First packet of frame %u at %u", packet.timestamp,
                           MaskWord64ToUWord32(nowMs));
            }
            renderTimeMs = _timing.RenderTimeMs(packet.timestamp, nowMs);
            if (renderTimeMs >= 0)
            {
                buffer->SetRenderTime(renderTimeMs);
            }
            else
            {
                buffer->SetRenderTime(nowMs);
            }
        }

        // Insert packet into jitter buffer
        // both media and empty packets
        const VCMFrameBufferEnum ret = _jitterBuffer.InsertPacket(buffer, packet);

        if (ret < 0)
        {
            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId),
                       "Error inserting packet seqNo=%u, timeStamp=%u",
                       packet.seqNum, packet.timestamp);
            return VCM_JITTER_BUFFER_ERROR;
        }
    }
    return VCM_OK;
}

VCMEncodedFrame*
VCMReceiver::FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs, WebRtc_Word64& nextRenderTimeMs,
                              bool renderTiming, VCMReceiver* dualReceiver)
{
    // No need to enter the critical section here since the jitter buffer
    // is thread-safe.
    FrameType incomingFrameType = kVideoFrameDelta;
    nextRenderTimeMs = -1;
    const WebRtc_Word64 startTimeMs = VCMTickTime::MillisecondTimestamp();
    WebRtc_Word64 ret = _jitterBuffer.GetNextTimeStamp(maxWaitTimeMs,
                                                       incomingFrameType,
                                                       nextRenderTimeMs);
    if (ret < 0)
    {
        // No timestamp in jitter buffer at the moment
        return NULL;
    }
    const WebRtc_UWord32 timeStamp = static_cast<WebRtc_UWord32>(ret);

    // Update the timing
    _timing.SetRequiredDelay(_jitterBuffer.GetEstimatedJitterMS());
    _timing.UpdateCurrentDelay(timeStamp);

    const WebRtc_Word32 tempWaitTime = maxWaitTimeMs -
            static_cast<WebRtc_Word32>(VCMTickTime::MillisecondTimestamp() - startTimeMs);
    WebRtc_UWord16 newMaxWaitTime = static_cast<WebRtc_UWord16>(VCM_MAX(tempWaitTime, 0));

    VCMEncodedFrame* frame = NULL;

    if (renderTiming)
    {
        frame = FrameForDecoding(newMaxWaitTime, nextRenderTimeMs, dualReceiver);
    }
    else
    {
        frame = FrameForRendering(newMaxWaitTime, nextRenderTimeMs, dualReceiver);
    }

    if (frame != NULL)
    {
        bool retransmitted = false;
        const WebRtc_Word64 lastPacketTimeMs =
                _jitterBuffer.LastPacketTime(frame, retransmitted);
        if (lastPacketTimeMs >= 0 && !retransmitted)
        {
            // We don't want to include timestamps which have suffered from retransmission
            // here, since we compensate with extra retransmission delay within
            // the jitter estimate.
            _timing.IncomingTimestamp(timeStamp, lastPacketTimeMs);
        }
        if (dualReceiver != NULL)
        {
            dualReceiver->UpdateState(*frame);
        }
    }
    return frame;
}

VCMEncodedFrame*
VCMReceiver::FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs,
                              WebRtc_Word64 nextRenderTimeMs,
                              VCMReceiver* dualReceiver)
{
    // How long can we wait until we must decode the next frame
    WebRtc_UWord32 waitTimeMs = _timing.MaxWaitingTime(nextRenderTimeMs,
                                          VCMTickTime::MillisecondTimestamp());

    // Try to get a complete frame from the jitter buffer
    VCMEncodedFrame* frame = _jitterBuffer.GetCompleteFrameForDecoding(0);

    if (frame == NULL && maxWaitTimeMs == 0 && waitTimeMs > 0)
    {
        // If we're not allowed to wait for frames to get complete we must
        // calculate if it's time to decode, and if it's not we will just return
        // for now.
        return NULL;
    }

    if (frame == NULL)
    {
        // Wait for a complete frame
        frame = _jitterBuffer.GetCompleteFrameForDecoding(maxWaitTimeMs);
    }
    if (frame == NULL)
    {
        // Get an incomplete frame
        if (_timing.MaxWaitingTime(nextRenderTimeMs,
                                   VCMTickTime::MillisecondTimestamp()) > 0)
        {
            // Still time to wait for a complete frame
            return NULL;
        }

        // No time left to wait, we must decode this frame now.
        const bool dualReceiverEnabledAndPassive = (dualReceiver != NULL &&
                                     dualReceiver->State() == kPassive &&
                                     dualReceiver->NackMode() == kNackInfinite);
        if (dualReceiverEnabledAndPassive &&
            !_jitterBuffer.CompleteSequenceWithNextFrame())
        {
            // Jitter buffer state might get corrupt with this frame.
            dualReceiver->CopyJitterBufferStateFromReceiver(*this);
        }

        frame = _jitterBuffer.GetFrameForDecoding();
    }
    return frame;
}

VCMEncodedFrame*
VCMReceiver::FrameForRendering(WebRtc_UWord16 maxWaitTimeMs,
                               WebRtc_Word64 nextRenderTimeMs,
                               VCMReceiver* dualReceiver)
{
    // How long MUST we wait until we must decode the next frame. This is different for the case
    // where we have a renderer which can render at a specified time. Here we must wait as long
    // as possible before giving the frame to the decoder, which will render the frame as soon
    // as it has been decoded.
    WebRtc_UWord32 waitTimeMs = _timing.MaxWaitingTime(nextRenderTimeMs,
                                                       VCMTickTime::MillisecondTimestamp());
    if (maxWaitTimeMs < waitTimeMs)
    {
        // If we're not allowed to wait until the frame is supposed to be rendered
        // we will have to return NULL for now.
        return NULL;
    }
    // Wait until it's time to render
    _renderWaitEvent.Wait(waitTimeMs);

    // Get a complete frame if possible
    VCMEncodedFrame* frame = _jitterBuffer.GetCompleteFrameForDecoding(0);

    if (frame == NULL)
    {
        // Get an incomplete frame
        const bool dualReceiverEnabledAndPassive = dualReceiver != NULL &&
                                                   dualReceiver->State() == kPassive &&
                                                   dualReceiver->NackMode() == kNackInfinite;
        if (dualReceiverEnabledAndPassive && !_jitterBuffer.CompleteSequenceWithNextFrame())
        {
            // Jitter buffer state might get corrupt with this frame.
            dualReceiver->CopyJitterBufferStateFromReceiver(*this);
        }

        frame = _jitterBuffer.GetFrameForDecoding();
    }
    return frame;
}

void
VCMReceiver::ReleaseFrame(VCMEncodedFrame* frame)
{
    _jitterBuffer.ReleaseFrame(frame);
}

WebRtc_Word32
VCMReceiver::ReceiveStatistics(WebRtc_UWord32& bitRate, WebRtc_UWord32& frameRate)
{
    const WebRtc_Word32 ret = _jitterBuffer.GetUpdate(frameRate, bitRate);
    bitRate /= 1000; // Should be in kbps
    return ret;
}

WebRtc_Word32
VCMReceiver::ReceivedFrameCount(VCMFrameCount& frameCount) const
{
    return _jitterBuffer.GetFrameStatistics(frameCount.numDeltaFrames,
                                            frameCount.numKeyFrames);
}

WebRtc_UWord32 VCMReceiver::DiscardedPackets() const {
  return _jitterBuffer.DiscardedPackets();
}

void
VCMReceiver::SetNackMode(VCMNackMode nackMode)
{
    CriticalSectionScoped cs(_critSect);
    _jitterBuffer.SetNackMode(nackMode);
    if (!_master)
    {
        _state = kPassive; // The dual decoder defaults to passive
    }
}

VCMNackMode
VCMReceiver::NackMode() const
{
    CriticalSectionScoped cs(_critSect);
    return _jitterBuffer.GetNackMode();
}

VCMNackStatus
VCMReceiver::NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size)
{
    bool extended = false;
    WebRtc_UWord16 nackListSize = 0;
    WebRtc_UWord16* internalNackList = _jitterBuffer.GetNackList(nackListSize, extended);
    if (internalNackList == NULL && nackListSize == 0xffff)
    {
        // This combination is used to trigger key frame requests.
        size = 0;
        return kNackKeyFrameRequest;
    }
    if (nackListSize > size)
    {
        size = nackListSize;
        return kNackNeedMoreMemory;
    }
    memcpy(nackList, internalNackList, nackListSize * sizeof(WebRtc_UWord16));
    size = nackListSize;
    return kNackOk;
}

// Decide whether we should change decoder state. This should be done if the dual decoder
// has caught up with the decoder decoding with packet losses.
bool
VCMReceiver::DualDecoderCaughtUp(VCMEncodedFrame* dualFrame, VCMReceiver& dualReceiver) const
{
    if (dualFrame == NULL)
    {
        return false;
    }
    if (_jitterBuffer.LastDecodedTimestamp() == dualFrame->TimeStamp())
    {
        dualReceiver.UpdateState(kWaitForPrimaryDecode);
        return true;
    }
    return false;
}

void
VCMReceiver::CopyJitterBufferStateFromReceiver(const VCMReceiver& receiver)
{
    _jitterBuffer = receiver._jitterBuffer;
}

VCMReceiverState
VCMReceiver::State() const
{
    CriticalSectionScoped cs(_critSect);
    return _state;
}

void
VCMReceiver::UpdateState(VCMReceiverState newState)
{
    CriticalSectionScoped cs(_critSect);
    assert(!(_state == kPassive && newState == kWaitForPrimaryDecode));
//    assert(!(_state == kReceiving && newState == kPassive));
    _state = newState;
}

void
VCMReceiver::UpdateState(VCMEncodedFrame& frame)
{
    if (_jitterBuffer.GetNackMode() == kNoNack)
    {
        // Dual decoder mode has not been enabled.
        return;
    }
    // Update the dual receiver state
    if (frame.Complete() && frame.FrameType() == kVideoFrameKey)
    {
        UpdateState(kPassive);
    }
    if (State() == kWaitForPrimaryDecode &&
        frame.Complete() && !frame.MissingFrame())
    {
        UpdateState(kPassive);
    }
    if (frame.MissingFrame() || !frame.Complete())
    {
        // State was corrupted, enable dual receiver.
        UpdateState(kReceiving);
    }
}

}
<|endoftext|>


<commit_before>#include<iostream>
#include<iomanip>

using namespace std;

/*
Variables
Son 20 Empleados
Leer a Cada uno
Nombre, Horas de Trabajo, Base de Pago, Edad

Otras Variables
Impuestos, Saldo Neto
*/

float calculo(float n1,float n2);

struct Trabajadores{
	string nombre; //Variable
	float horas; //Variable
	float pago; //Variable
	int edad; //Variable
	float impuestos; //Variable Secundaria
	float sneto; //Variable Secundaria
}T[20];

int menu(){
	for(int j=1;j<=3;j++){
		for(int i=1;i<=3;i++){
//			cout<<"Introducir Nombre de Empleado #"<<i<<": ";
//			cin>>T[i].nombre;
//			cout<<"Introducir Edad de Empleado #"<<i<<": ";
//			cin>>T[i].edad;
			cout<<"Introducir Base de Pago de Empleado #"<<i<<": ";
			cin>>T[i].pago;
			cout<<"Introducir Horas de Empleado #"<<i<<": ";
			cin>>T[i].horas;
		}
		calculo(T[j].pago,T[j].horas);
	}
}

float calculo(float n1, float n2){
	for(int i=1;i<=3;i++){
		cout<<T[i].pago<<endl;
		cout<<T[i].horas<<endl;
	}
}

int main(){
	menu();
}
<commit_msg>Corrigiendo Coflicto<commit_after>#include<iostream>
#include<iomanip>

using namespace std;

/*
Variables
Son 20 Empleados
Leer a Cada uno
Nombre, Horas de Trabajo, Base de Pago, Edad

Otras Variables
Impuestos, Saldo Neto
*/

float calculo(float n1,float n2);

struct Trabajadores{
	string nombre; //Variable
	float horas; //Variable
	float pago; //Variable
	int edad; //Variable
	float impuestos; //Variable Secundaria
	float sneto; //Variable Secundaria
}T[20];

void menu(){
	for(int j=1;j<=3;j++){
		for(int i=1;i<=3;i++){
			cout<<"Introducir Nombre de Empleado #"<<i<<": ";
			cin>>T[i].nombre;
			cout<<"Introducir Edad de Empleado #"<<i<<": ";
			cin>>T[i].edad;
			cout<<"Introducir Base de Pago de Empleado #"<<i<<": ";
			cin>>T[i].pago;
			cout<<"Introducir Horas de Empleado #"<<i<<": ";
			cin>>T[i].horas;
		}
		calculo(T[j].pago,T[j].horas);
	}
}

float calculo(float n1, float n2){
	for(int i=1;i<=3;i++){
		cout<<T[i].pago<<endl;
		cout<<T[i].horas<<endl;
	}
}

int main(){
	menu();
}
<|endoftext|>