/*--------------------------------------------------------------------------------*/
bool AudioObjectParameters::operator == (const AudioObjectParameters& obj) const
{
  bool same = ((position == obj.position) &&
               (memcmp(&values, &obj.values, sizeof(obj.values)) == 0) &&
               Compare(excludedZones, obj.excludedZones) &&
               (GetMinPosition() == obj.GetMinPosition()) &&
               (GetMaxPosition() == obj.GetMaxPosition()) &&
               (othervalues == obj.othervalues));
#if BBCDEBUG_LEVEL>=4
  if (!same)
  {
    BBCDEBUG("Compare: %u/%u/%u/%u/%08x/%08x", (uint_t)(position == obj.position), (uint_t)(memcmp(&values, &obj.values, sizeof(obj.values)) == 0), (uint_t)Compare(excludedZones, obj.excludedZones), (uint_t)(othervalues == obj.othervalues), setbitmap, obj.setbitmap);

    std::string line;
    const uint8_t *p1 = (const uint8_t *)&values, *p2 = (const uint8_t *)&obj.values;
    uint_t i;
    for (i = 0; i < sizeof(values); i++)
    {
      if (line.empty())
      {
        Printf(line, "%04x:", i);
      }
      Printf(line, " %02x/%02x", (uint_t)p1[i], (uint_t)p2[i]);
      if ((i & 7) == 7)
      {
        BBCDEBUG("%s", line.c_str());
        line = "";
      }
    }
    if (!line.empty()) BBCDEBUG("%s", line.c_str());
  }
#endif
  return same;
}
/*--------------------------------------------------------------------------------*/
void AudioObjectParameters::Interpolate(AudioObjectParameters& dst, double mul, const AudioObjectParameters& a, const AudioObjectParameters& b)
{
  dst = (mul >= .5f) ? a : b; // best initial value for non-interpolatable parameters

  // now modify parameters that are interpolatable
  // outside of the endstops, actually interpolate
  if ((mul > 0.0) && (mul < 1.0))
  {
    // merge bitmaps of parameters set so that if parameter is set in *either* a or b it will be interpolated
    dst.setbitmap = a.setbitmap | b.setbitmap;

    if (dst.IsParameterSet(Parameter_position))
    {
      dst.Interpolate(Parameter_position, mul, dst.position, &a.position, &b.position);
    }
    if (dst.IsParameterSet(Parameter_minposition))
    {
      if (!dst.minposition) dst.minposition = new Position;
      dst.Interpolate(Parameter_minposition, mul, *dst.minposition, a.minposition, b.minposition);
    }
    if (dst.IsParameterSet(Parameter_maxposition))
    {
      if (!dst.maxposition) dst.maxposition = new Position;
      dst.Interpolate(Parameter_maxposition, mul, *dst.maxposition, a.maxposition, b.maxposition);
    }
    
    dst.Interpolate<>(Parameter_gain, mul, dst.values.gain, a.values.gain, b.values.gain);
    dst.Interpolate<>(Parameter_width, mul, dst.values.width, a.values.width, b.values.width);
    dst.Interpolate<>(Parameter_height, mul, dst.values.height, a.values.height, b.values.height);
    dst.Interpolate<>(Parameter_depth, mul, dst.values.depth, a.values.depth, b.values.depth);
    dst.Interpolate<>(Parameter_divergencebalance, mul, dst.values.divergencebalance, a.values.divergencebalance, b.values.divergencebalance);
    dst.Interpolate<>(Parameter_divergenceazimuth, mul, dst.values.divergenceazimuth, a.values.divergenceazimuth, b.values.divergenceazimuth);
    dst.Interpolate<>(Parameter_diffuseness, mul, dst.values.diffuseness, a.values.diffuseness, b.values.diffuseness);
    dst.Interpolate<>(Parameter_channellockmaxdistance, mul, dst.values.channellockmaxdistance, a.values.channellockmaxdistance, b.values.channellockmaxdistance);
  }
}
int main(int argc, char *argv[])
{
  // print library versions (the actual loaded versions, if dynamically linked)
  printf("Versions:\n%s\n", LoadedVersions::Get().GetVersionsList().c_str());

  if (argc < 2)
  {
    fprintf(stderr, "Usage: write-separate-adm <filename>\n");
    exit(1);
  }
  
  // create basic ADM
  XMLADMData *adm;
  if ((adm = XMLADMData::CreateADM()) != NULL)
  {
    ADMData::OBJECTNAMES names;

    // set programme name
    // if an audioProgramme object of this name doesn't exist, one will be created
    names.programmeName = "ADM Test Programme";

    // set content name
    // if an audioContent object of this name doesn't exist, one will be created
    names.contentName   = "ADM Test Content";
    
    // create 16 tracks, channels and streams
    uint_t t, ntracks = 16;
    for (t = 0; t < ntracks; t++)
    {
      ADMAudioTrack *track;
      std::string trackname;
      
      printf("------------- Track %2u -----------------\n", t + 1);

      // create default audioTrackFormat name (used for audioStreamFormat objects as well)
      Printf(trackname, "Track %u", t + 1);
      
      names.trackNumber = t;

      // create audioTrackUID for chna chunk
      if ((track = adm->CreateTrack(names.trackNumber)) != NULL)
      {
        track->SetSampleRate(48000);
        track->SetBitDepth(24);
      }

      // derive channel and stream names from track name
      names.channelFormatName = trackname;
      names.streamFormatName  = "PCM_" + trackname;
      names.trackFormatName   = "PCM_" + trackname;

      // set object name
      // create 4 objects, each of 4 tracks
      names.objectName = "";  // need this because Printf() APPENDS!
      Printf(names.objectName, "Object %u", 1 + (t / 4));
        
      // set pack name from object name
      // create 4 packs, each of 4 tracks
      names.packFormatName = "";  // need this because Printf() APPENDS!
      Printf(names.packFormatName, "Pack %u", 1 + (t / 4));

      adm->CreateObjects(names);

      // note how the programme and content names are left in place in 'names'
      // this is necessary to ensure that things are linked up properly

      // find channel format object for this track
      ADMAudioChannelFormat *cf;
      if ((cf = dynamic_cast<ADMAudioChannelFormat *>(adm->GetWritableObjectByName(names.channelFormatName, ADMAudioChannelFormat::Type))) != NULL)
      {
        // found channel format, generate block formats for it
        ADMAudioBlockFormat   *bf;
        uint_t i;

        for (i = 0; i < 20; i++)
        {
          if ((bf = adm->CreateBlockFormat(cf)) != NULL)
          {
            AudioObjectParameters params;
            Position pos;
              
            // set start time to be index * 25ms
            bf->SetRTime(i * 25000000);
            // set duration to be 25ms
            bf->SetDuration(25000000);

            pos.polar  = true;
            pos.pos.az = fmod((double)(t + i) * 20.0, 360.0);
            pos.pos.el = (double)i / (double)ntracks * 60.0;
            pos.pos.d  = 1.0;
            params.SetPosition(pos);

            params.SetGain(2.0);
            params.SetWidth(5.0);
            params.SetHeight(10.0);
            params.SetDepth(15.0);
            params.SetDiffuseness(20.0);
            params.SetDelay(25.0);
            params.SetObjectImportance(5);
            params.SetChannelImportance(2);
            params.SetDialogue(1);
            params.SetChannelLock(true);
            params.SetInteract(true);
            params.SetInterpolate(true);
            params.SetInterpolationTimeS(5.2);
            params.SetOnScreen(true);
            ParameterSet othervalues = params.GetOtherValues();
            params.SetOtherValues(othervalues.Set("other1", 1).Set("other2", "2"));

            // set object parameters
            bf->GetObjectParameters() = params;
          }
          else fprintf(stderr, "Failed to create audioBlockFormat (t: %u, i: %u)\n", t, i);
        }
      }
      else
      {
        // this will only occur in the case of an error
        fprintf(stderr, "Unable to find channel format '%s'\n", names.channelFormatName.c_str());
      }
    }

    // finalise ADM
    adm->Finalise();

    // output ADM
    std::string axml = adm->GetAxml();
    printf("XML:\n%s", axml.c_str());

    // create simple WAV file
    RIFFFile file;
    if (file.Create(argv[1], 48000, 16))
    {
      // add the chna and axml chunks from the ADM
      uint64_t len;
      const uint8_t *data;

      // add chna chunk
      if ((data = adm->GetChna(len)) != NULL) file.AddChunk("chna", data, len);
      else fprintf(stderr, "No chna chunk to add!\n");

      // add axml chunk
      file.AddChunk("axml", (const uint8_t *)axml.c_str(), axml.size());

      // write everything
      file.Close();
    }
    else fprintf(stderr, "Failed to create file '%s'\n", argv[1]);
  }
  else fprintf(stderr, "Failed to create ADM - no provider available?\n");

  return 0;
}
/*--------------------------------------------------------------------------------*/
AudioObjectParameters operator * (const AudioObjectParameters& obj, const PositionTransform& transform)
{
  AudioObjectParameters res = obj;
  res.SetPosition(obj.GetPosition() * transform);
  return res;
}
int main(void)
{
  // print library versions (the actual loaded versions, if dynamically linked)
  printf("Versions:\n%s\n", LoadedVersions::Get().GetVersionsList().c_str());

  XMLADMData *adm;
  
  // create basic ADM
  if ((adm = XMLADMData::CreateADM()) != NULL)
  {
    ADMData::OBJECTNAMES names;

    // set programme name
    // if an audioProgramme object of this name doesn't exist, one will be created
    names.programmeName = "ADM Test Programme";

    // set content name
    // if an audioContent object of this name doesn't exist, one will be created
    names.contentName   = "ADM Test Content";
    
    // create 16 tracks, channels and streams
    uint_t t, ntracks = 16;
    for (t = 0; t < ntracks; t++)
    {
      std::string trackname;

      printf("------------- Track %2u -----------------\n", t + 1);

      // create default audioTrackFormat name (used for audioStreamFormat objects as well)
      Printf(trackname, "Track %u", t + 1);

      names.trackNumber = t;

      // derive channel and stream names from track name
      names.channelFormatName = trackname;
      names.streamFormatName  = "PCM_" + trackname;
      names.trackFormatName   = "PCM_" + trackname;

      // set object name
      // create 4 objects, each of 4 tracks
      names.objectName = "";  // need this because Printf() APPENDS!
      Printf(names.objectName, "Object %u", 1 + (t / 4));
        
      // set pack name from object name
      // create 4 packs, each of 4 tracks
      names.packFormatName = "";  // need this because Printf() APPENDS!
      Printf(names.packFormatName, "Pack %u", 1 + (t / 4));

      adm->CreateObjects(names);

      // note how the programme and content names are left in place in 'names'
      // this is necessary to ensure that things are linked up properly

      // find channel format object for this track
      ADMAudioChannelFormat *cf;
      if ((cf = dynamic_cast<ADMAudioChannelFormat *>(adm->GetWritableObjectByName(names.channelFormatName, ADMAudioChannelFormat::Type))) != NULL)
      {
        // found channel format, generate block formats for it
        ADMAudioBlockFormat   *bf;
        uint_t i;

        for (i = 0; i < 20; i++)
        {
          if ((bf = adm->CreateBlockFormat(cf)) != NULL)
          {
            AudioObjectParameters params;
            Position pos;
              
            // set start time to be index * 25ms
            bf->SetRTime(i * 25000000);
            // set duration to be 25ms
            bf->SetDuration(25000000);

            pos.polar  = true;
            pos.pos.az = fmod((double)(t + i) * 20.0, 360.0);
            pos.pos.el = (double)i / (double)ntracks * 60.0;
            pos.pos.d  = 1.0;
            params.SetPosition(pos);

            params.SetGain(2.0);
            params.SetWidth(5.0);
            params.SetHeight(10.0);
            params.SetDepth(15.0);
            params.SetDiffuseness(20.0);
            params.SetDelay(25.0);
            params.SetObjectImportance(5);
            params.SetChannelImportance(2);
            params.SetDialogue(1);
            params.SetChannelLock(true);
            params.SetInteract(true);
            params.SetInterpolate(true);
            params.SetInterpolationTimeS(5.2);
            params.SetOnScreen(true);
            ParameterSet othervalues = params.GetOtherValues();
            params.SetOtherValues(othervalues.Set("other1", 1).Set("other2", "2"));

            // set object parameters
            bf->GetObjectParameters() = params;
          }
          else fprintf(stderr, "Failed to create audioBlockFormat (t: %u, i: %u)\n", t, i);
        }
      }
      else
      {
        // this will only occur in the case of an error
        fprintf(stderr, "Unable to find channel format '%s'\n", names.channelFormatName.c_str());
      }
    }

    // finalise ADM
    adm->Finalise();
    
    // output ADM
    printf("XML:\n%s", adm->GetAxml().c_str());
  }

  return 0;
}
int main(void)
{
  // ensure libraries are set up
  bbcat_register_bbcat_fileio();

  // ADM aware WAV file
  ADMRIFFFile file;
  const char *filename = "adm-4gb-bwf.wav";
  
  // IMPORTANT: create basic ADM here - if this is not done, the file will be a plain WAV file!
  file.CreateADM();

  // create file
  if (file.Create(filename, 48000, 32))
  {
    ADMData *adm = file.GetADM();

    if (adm)
    {
      ADMData::OBJECTNAMES names;

      printf("Created '%s' okay, %u channels at %luHz (%u bytes per sample)\n", filename, file.GetChannels(), (ulong_t)file.GetSampleRate(), (uint_t)file.GetBytesPerSample());

      // set programme name
      // if an audioProgramme object of this name doesn't exist, one will be created
      names.programmeName = "ADM Test Programme";

      // set content name
      // if an audioContent object of this name doesn't exist, one will be created
      names.contentName   = "ADM Test Content";
    
      // create tracks, channels and streams
      uint_t t;
      const ADMData::TRACKLIST& tracklist = adm->GetTrackList();
      for (t = 0; t < tracklist.size(); t++)
      {
        std::string trackname;

        printf("------------- Track %2u -----------------\n", t + 1);

        // create default audioTrackFormat name (used for audioStreamFormat objects as well)
        Printf(trackname, "Track %u", t + 1);

        names.trackNumber = t;

        // derive channel and stream names from track name
        names.channelFormatName = trackname;
        names.streamFormatName  = "PCM_" + trackname;
        names.trackFormatName   = "PCM_" + trackname;

        // set object name
        // create 4 objects, each of 4 tracks
        names.objectName = "";  // need this because Printf() APPENDS!
        Printf(names.objectName, "Object %u", 1 + (t / 4));
        
        // set pack name from object name
        // create 4 packs, each of 4 tracks
        names.packFormatName = "";  // need this because Printf() APPENDS!
        Printf(names.packFormatName, "Pack %u", 1 + (t / 4));

        // set default typeLabel
        names.typeLabel = ADMObject::TypeLabel_Objects;

        adm->CreateObjects(names);

        // note how the programme and content names are left in place in 'names'
        // this is necessary to ensure that things are linked up properly
      }
    }
    else fprintf(stderr, "File does not have an ADM associated with it, did you forget to create one?\n");
    
    // write audio
    const uint_t nchannels = file.GetChannels();
    std::vector<sint32_t> audio;
    double fs = (double)file.GetSampleRate();
    double level = dBToGain(-40.0);
    uint64_t i, nsamples = (uint64_t)1000 * (uint64_t)file.GetSampleRate();
    uint_t step = file.GetSampleRate() * 10;
    uint_t j, pc = ~0;

    UNUSED_PARAMETER(fs);
    UNUSED_PARAMETER(level);
    
    audio.resize(step * nchannels);

    // write 1000s worth of samples
    for (i = 0; i < nsamples; i += step)
    {
      {
        uint_t index = (uint_t)(i / step);

        for (j = 0; j < nchannels; j++)
        {
          AudioObjectParameters params;
          Position pos;

          pos.polar  = true;
          pos.pos.az = (double)(index + j) * 20.0;
          pos.pos.el = (double)j / (double)file.GetChannels() * 60.0;
          pos.pos.d  = 1.0;
          params.SetPosition(pos);

          // set some extra parameters
          params.SetWidth((float)j * .2);
          params.SetDepth((float)j * .4);
          params.SetHeight((float)j * .3);
          
          if (j < 2)
          {
            params.SetScreenEdgeLock("azimuth", j ? "right" : "left");
            params.SetScreenEdgeLock("elevation", j ? "bottom" : "top");
          }
          else if (j == 2)
          {
            params.AddExcludedZone("left",  -1, -1, -1, -.8, 1, 1);
            params.AddExcludedZone("right", .8, -1, -1, 1.0, 1, 1);
          }
          else
          {
            params.SetObjectImportance(5 + j - 3);
            params.SetChannelImportance(2 + j - 3);
            params.SetInteract(j != 3);
            params.SetDisableDucking(j == 3);
          }
          file.SetObjectParameters(j, params);
        }
      }
      
      // write a frame of audio
      file.WriteSamples(&audio[0], 0, nchannels, step);

      uint_t pc1 = (uint_t)((i * 100) / nsamples);
      if (pc1 != pc)
      {
        pc = pc1;
        printf("\rWriting... %u%% done", pc);
        fflush(stdout);
      }
    }

    printf("\n");

    file.Close();
  }
  else fprintf(stderr, "Failed to open file '%s' for writing!\n", filename);
  
  return 0;
}