/**********************************************************************

  Audacity: A Digital Audio Editor

  @file MixerOptions.cpp

  Dominic Mazzoni
  Markus Meyer
  Vaughan Johnson

  Paul Licameli split from Mix.cpp

**********************************************************************/
#include "MixerOptions.h"

#include "Envelope.h"
#include "SampleTrack.h"

MixerOptions::Warp::Warp(const TrackList &list)
: envelope(DefaultWarp::Call(list)), minSpeed(0.0), maxSpeed(0.0)
{
}

MixerOptions::Warp::Warp(const BoundedEnvelope *e)
    : envelope(e), minSpeed(0.0), maxSpeed(0.0)
 {}

MixerOptions::Warp::Warp(double min, double max, double initial)
   : minSpeed{ std::max(0.0, std::min(min, max)) }
   , maxSpeed{ std::max(0.0, std::max(min, max)) }
   , initialSpeed{initial}
{
   assert(min >= 0);
   assert(max >= 0);
   assert(min <= max);
}

MixerOptions::ResampleParameters::ResampleParameters(bool highQuality,
   const SampleTrack &leader, double rate, const Warp &options
)  : mHighQuality{ highQuality }
{
   auto range = TrackList::Channels<const SampleTrack>(&leader);
   auto size = range.size();
   mMinFactor.reserve(size);
   mMaxFactor.reserve(size);
   for (auto pTrack : range) {
      double factor = (rate / pTrack->GetRate());
      if (const auto envelope = options.envelope) {
         // variable rate resampling
         mVariableRates = true;
         mMinFactor.push_back(factor / envelope->GetRangeUpper());
         mMaxFactor.push_back(factor / envelope->GetRangeLower());
      }
      else if (options.minSpeed > 0.0 && options.maxSpeed > 0.0) {
         // variable rate resampling
         mVariableRates = true;
         mMinFactor.push_back(factor / options.maxSpeed);
         mMaxFactor.push_back(factor / options.minSpeed);
      }
      else {
         // constant rate resampling
         mVariableRates = false;
         mMinFactor.push_back(factor);
         mMaxFactor.push_back(factor);
      }
   }
}

MixerOptions::Downmix::Downmix(unsigned numTracks, unsigned maxNumChannels)
{
   mNumTracks = mNumChannels = numTracks;
   mMaxNumChannels = maxNumChannels;

   if( mNumChannels > mMaxNumChannels )
         mNumChannels = mMaxNumChannels;

   Alloc();

   for( unsigned int i = 0; i < mNumTracks; i++ )
      for( unsigned int j = 0; j < mNumChannels; j++ )
         mMap[ i ][ j ] = ( i == j );
}

MixerOptions::Downmix::Downmix(const Downmix &mixerSpec)
{
   mNumTracks = mixerSpec.mNumTracks;
   mMaxNumChannels = mixerSpec.mMaxNumChannels;
   mNumChannels = mixerSpec.mNumChannels;

   Alloc();

   for( unsigned int i = 0; i < mNumTracks; i++ )
      for( unsigned int j = 0; j < mNumChannels; j++ )
         mMap[ i ][ j ] = mixerSpec.mMap[ i ][ j ];
}

void MixerOptions::Downmix::Alloc()
{
   mMap.reinit(mNumTracks, mMaxNumChannels);
}

MixerOptions::Downmix::~Downmix()
{
}

bool MixerOptions::Downmix::SetNumChannels(unsigned newNumChannels)
{
   if( mNumChannels == newNumChannels )
      return true;

   if( newNumChannels > mMaxNumChannels )
      return false;

   for( unsigned int i = 0; i < mNumTracks; i++ )
   {
      for( unsigned int j = newNumChannels; j < mNumChannels; j++ )
         mMap[ i ][ j ] = false;

      for( unsigned int j = mNumChannels; j < newNumChannels; j++ )
         mMap[ i ][ j ] = false;
   }

   mNumChannels = newNumChannels;
   return true;
}

auto MixerOptions::Downmix::operator=(const Downmix &mixerSpec) -> Downmix &
{
   mNumTracks = mixerSpec.mNumTracks;
   mNumChannels = mixerSpec.mNumChannels;
   mMaxNumChannels = mixerSpec.mMaxNumChannels;

   Alloc();

   for( unsigned int i = 0; i < mNumTracks; i++ )
      for( unsigned int j = 0; j < mNumChannels; j++ )
         mMap[ i ][ j ] = mixerSpec.mMap[ i ][ j ];

   return *this;
}
