/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "third_party/blink/renderer/platform/audio/audio_bus.h"

#include <assert.h>
#include <math.h>

#include <algorithm>
#include <array>
#include <memory>

#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/numerics/safe_conversions.h"
#include "media/base/audio_bus.h"
#include "media/base/sinc_resampler.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_audio_bus.h"
#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
#include "third_party/blink/renderer/platform/audio/vector_math.h"
#include "third_party/blink/renderer/platform/wtf/shared_buffer.h"

namespace blink {

using vector_math::Vadd;
using vector_math::Vsma;

constexpr unsigned kMaxBusChannels = 32;

scoped_refptr<AudioBus> AudioBus::Create(unsigned number_of_channels,
                                         uint32_t length,
                                         bool allocate) {
  CHECK_LE(number_of_channels, kMaxBusChannels);

  if (allocate) {
    scoped_refptr<AudioBus> bus = TryCreate(number_of_channels, length);
    CHECK(bus);
    return bus;
  }

  return base::AdoptRef(new AudioBus(number_of_channels, length, false));
}

scoped_refptr<AudioBus> AudioBus::TryCreate(unsigned number_of_channels,
                                            uint32_t length) {
  if (number_of_channels > kMaxBusChannels) {
    return nullptr;
  }

  scoped_refptr<AudioBus> bus =
      base::AdoptRef(new AudioBus(number_of_channels, length, false));

  for (AudioChannel& channel : bus->channels_) {
    if (!channel.TryAllocate(length)) {
      return nullptr;
    }
  }

  return bus;
}

AudioBus::AudioBus(unsigned number_of_channels, uint32_t length, bool allocate)
    : length_(length), sample_rate_(0) {
  channels_.ReserveInitialCapacity(number_of_channels);

  for (unsigned i = 0; i < number_of_channels; ++i) {
    if (allocate) {
      channels_.emplace_back(length);
    } else {
      channels_.emplace_back();
    }
  }

  layout_ = kLayoutCanonical;  // for now this is the only layout we define
}

void AudioBus::SetChannelMemory(unsigned channel_index,
                                base::span<float> storage) {
  if (channel_index < channels_.size()) {
    Channel(channel_index)->Set(storage);
    length_ = base::checked_cast<uint32_t>(storage.size());
  }
}

void AudioBus::ResizeSmaller(uint32_t new_length) {
  DCHECK_LE(new_length, length_);
  if (new_length <= length_) {
    length_ = new_length;
  }

  for (AudioChannel& channel : channels_) {
    channel.ResizeSmaller(new_length);
  }
}

void AudioBus::Zero() {
  for (AudioChannel& channel : channels_) {
    channel.Zero();
  }
}

const AudioChannel* AudioBus::ChannelByType(unsigned channel_type) const {
  // For now we only support canonical channel layouts...
  if (layout_ != kLayoutCanonical) {
    return nullptr;
  }

  switch (NumberOfChannels()) {
    case 1:  // mono
      if (channel_type == kChannelMono || channel_type == kChannelLeft) {
        return Channel(0);
      }
      return nullptr;

    case 2:  // stereo
      switch (channel_type) {
        case kChannelLeft:
          return Channel(0);
        case kChannelRight:
          return Channel(1);
        default:
          return nullptr;
      }

    case 4:  // quad
      switch (channel_type) {
        case kChannelLeft:
          return Channel(0);
        case kChannelRight:
          return Channel(1);
        case kChannelSurroundLeft:
          return Channel(2);
        case kChannelSurroundRight:
          return Channel(3);
        default:
          return nullptr;
      }

    case 5:  // 5.0
      switch (channel_type) {
        case kChannelLeft:
          return Channel(0);
        case kChannelRight:
          return Channel(1);
        case kChannelCenter:
          return Channel(2);
        case kChannelSurroundLeft:
          return Channel(3);
        case kChannelSurroundRight:
          return Channel(4);
        default:
          return nullptr;
      }

    case 6:  // 5.1
      switch (channel_type) {
        case kChannelLeft:
          return Channel(0);
        case kChannelRight:
          return Channel(1);
        case kChannelCenter:
          return Channel(2);
        case kChannelLFE:
          return Channel(3);
        case kChannelSurroundLeft:
          return Channel(4);
        case kChannelSurroundRight:
          return Channel(5);
        default:
          return nullptr;
      }
  }

  NOTREACHED();
}

AudioChannel* AudioBus::ChannelByType(unsigned type) {
  return const_cast<AudioChannel*>(
      static_cast<const AudioBus*>(this)->ChannelByType(type));
}

// Returns true if the channel count and frame-size match.
bool AudioBus::TopologyMatches(const AudioBus& bus) const {
  if (NumberOfChannels() != bus.NumberOfChannels()) {
    return false;  // channel mismatch
  }

  // Make sure source bus has enough frames.
  if (length() > bus.length()) {
    return false;  // frame-size mismatch
  }

  return true;
}

scoped_refptr<AudioBus> AudioBus::CreateBufferFromRange(
    const AudioBus* source_buffer,
    unsigned start_frame,
    unsigned end_frame) {
  uint32_t number_of_source_frames = source_buffer->length();
  unsigned number_of_channels = source_buffer->NumberOfChannels();

  // Sanity checking
  bool is_range_safe =
      start_frame < end_frame && end_frame <= number_of_source_frames;
  DCHECK(is_range_safe);
  if (!is_range_safe) {
    return nullptr;
  }

  uint32_t range_length = end_frame - start_frame;

  scoped_refptr<AudioBus> audio_bus =
      TryCreate(number_of_channels, range_length);
  if (!audio_bus) {
    return nullptr;
  }

  audio_bus->SetSampleRate(source_buffer->SampleRate());

  for (unsigned i = 0; i < number_of_channels; ++i) {
    audio_bus->Channel(i)->CopyFromRange(source_buffer->Channel(i), start_frame,
                                         end_frame);
  }

  return audio_bus;
}

float AudioBus::MaxAbsValue() const {
  float max = 0.0f;
  for (const AudioChannel& channel : channels_) {
    max = std::max(max, channel.MaxAbsValue());
  }

  return max;
}

void AudioBus::Normalize() {
  float max = MaxAbsValue();
  if (max) {
    Scale(1.0f / max);
  }
}

void AudioBus::Scale(float scale) {
  for (AudioChannel& channel : channels_) {
    channel.Scale(scale);
  }
}

void AudioBus::CopyFrom(const AudioBus& source_bus,
                        ChannelInterpretation channel_interpretation) {
  if (&source_bus == this) {
    return;
  }

  // Copying bus is equivalent to zeroing and then summing.
  Zero();
  SumFrom(source_bus, channel_interpretation);
}

void AudioBus::SumFrom(const AudioBus& source_bus,
                       ChannelInterpretation channel_interpretation) {
  if (&source_bus == this) {
    return;
  }

  unsigned number_of_source_channels = source_bus.NumberOfChannels();
  unsigned number_of_destination_channels = NumberOfChannels();

  // If the channel numbers are equal, perform channels-wise summing.
  if (number_of_source_channels == number_of_destination_channels) {
    for (unsigned i = 0; i < number_of_source_channels; ++i) {
      Channel(i)->SumFrom(source_bus.Channel(i));
    }

    return;
  }

  // Otherwise perform up/down-mix or the discrete transfer based on the
  // number of channels and the channel interpretation.
  switch (channel_interpretation) {
    case kSpeakers:
      if (number_of_source_channels < number_of_destination_channels) {
        SumFromByUpMixing(source_bus);
      } else {
        SumFromByDownMixing(source_bus);
      }
      break;
    case kDiscrete:
      DiscreteSumFrom(source_bus);
      break;
  }
}

void AudioBus::DiscreteSumFrom(const AudioBus& source_bus) {
  DCHECK_NE(NumberOfChannels(), source_bus.NumberOfChannels());
  unsigned count = std::min(NumberOfChannels(), source_bus.NumberOfChannels());
  for (unsigned i = 0; i < count; ++i) {
    Channel(i)->SumFrom(source_bus.Channel(i));
  }
}

void AudioBus::SumFromByUpMixing(const AudioBus& source_bus) {
  unsigned number_of_source_channels = source_bus.NumberOfChannels();
  unsigned number_of_destination_channels = NumberOfChannels();

  if ((number_of_source_channels == 1 && number_of_destination_channels == 2) ||
      (number_of_source_channels == 1 && number_of_destination_channels == 4)) {
    // Up-mixing: 1 -> 2, 1 -> 4
    //   output.L = input
    //   output.R = input
    //   output.SL = 0 (in the case of 1 -> 4)
    //   output.SR = 0 (in the case of 1 -> 4)
    const AudioChannel* source_l = source_bus.ChannelByType(kChannelLeft);
    ChannelByType(kChannelLeft)->SumFrom(source_l);
    ChannelByType(kChannelRight)->SumFrom(source_l);
  } else if (number_of_source_channels == 1 &&
             number_of_destination_channels == 6) {
    // Up-mixing: 1 -> 5.1
    //   output.L = 0
    //   output.R = 0
    //   output.C = input (put in center channel)
    //   output.LFE = 0
    //   output.SL = 0
    //   output.SR = 0
    ChannelByType(kChannelCenter)
        ->SumFrom(source_bus.ChannelByType(kChannelLeft));
  } else if ((number_of_source_channels == 2 &&
              number_of_destination_channels == 4) ||
             (number_of_source_channels == 2 &&
              number_of_destination_channels == 6)) {
    // Up-mixing: 2 -> 4, 2 -> 5.1
    //   output.L = input.L
    //   output.R = input.R
    //   output.C = 0 (in the case of 2 -> 5.1)
    //   output.LFE = 0 (in the case of 2 -> 5.1)
    //   output.SL = 0
    //   output.SR = 0
    ChannelByType(kChannelLeft)
        ->SumFrom(source_bus.ChannelByType(kChannelLeft));
    ChannelByType(kChannelRight)
        ->SumFrom(source_bus.ChannelByType(kChannelRight));
  } else if (number_of_source_channels == 4 &&
             number_of_destination_channels == 6) {
    // Up-mixing: 4 -> 5.1
    //   output.L = input.L
    //   output.R = input.R
    //   output.C = 0
    //   output.LFE = 0
    //   output.SL = input.SL
    //   output.SR = input.SR
    ChannelByType(kChannelLeft)
        ->SumFrom(source_bus.ChannelByType(kChannelLeft));
    ChannelByType(kChannelRight)
        ->SumFrom(source_bus.ChannelByType(kChannelRight));
    ChannelByType(kChannelSurroundLeft)
        ->SumFrom(source_bus.ChannelByType(kChannelSurroundLeft));
    ChannelByType(kChannelSurroundRight)
        ->SumFrom(source_bus.ChannelByType(kChannelSurroundRight));
  } else {
    // All other cases, fall back to the discrete sum. This will silence the
    // excessive channels.
    DiscreteSumFrom(source_bus);
  }
}

void AudioBus::SumFromByDownMixing(const AudioBus& source_bus) {
  unsigned number_of_source_channels = source_bus.NumberOfChannels();
  unsigned number_of_destination_channels = NumberOfChannels();

  if (number_of_source_channels == 2 && number_of_destination_channels == 1) {
    // Down-mixing: 2 -> 1
    //   output = 0.5 * (input.L + input.R)
    base::span<const float> source_l =
        source_bus.ChannelByType(kChannelLeft)->Span();
    base::span<const float> source_r =
        source_bus.ChannelByType(kChannelRight)->Span();

    base::span<float> destination = ChannelByType(kChannelLeft)->MutableSpan();
    float scale = 0.5;

    Vsma(source_l, scale, destination, length());
    Vsma(source_r, scale, destination, length());
  } else if (number_of_source_channels == 4 &&
             number_of_destination_channels == 1) {
    // Down-mixing: 4 -> 1
    //   output = 0.25 * (input.L + input.R + input.SL + input.SR)
    base::span<const float> source_l =
        source_bus.ChannelByType(kChannelLeft)->Span();
    base::span<const float> source_r =
        source_bus.ChannelByType(kChannelRight)->Span();
    base::span<const float> source_sl =
        source_bus.ChannelByType(kChannelSurroundLeft)->Span();
    base::span<const float> source_sr =
        source_bus.ChannelByType(kChannelSurroundRight)->Span();

    base::span<float> destination = ChannelByType(kChannelLeft)->MutableSpan();
    float scale = 0.25;

    Vsma(source_l, scale, destination, length());
    Vsma(source_r, scale, destination, length());
    Vsma(source_sl, scale, destination, length());
    Vsma(source_sr, scale, destination, length());
  } else if (number_of_source_channels == 6 &&
             number_of_destination_channels == 1) {
    // Down-mixing: 5.1 -> 1
    //   output = sqrt(1/2) * (input.L + input.R) + input.C
    //            + 0.5 * (input.SL + input.SR)
    base::span<const float> source_l =
        source_bus.ChannelByType(kChannelLeft)->Span();
    base::span<const float> source_r =
        source_bus.ChannelByType(kChannelRight)->Span();
    base::span<const float> source_c =
        source_bus.ChannelByType(kChannelCenter)->Span();
    base::span<const float> source_sl =
        source_bus.ChannelByType(kChannelSurroundLeft)->Span();
    base::span<const float> source_sr =
        source_bus.ChannelByType(kChannelSurroundRight)->Span();

    base::span<float> destination = ChannelByType(kChannelLeft)->MutableSpan();
    float scale_sqrt_half = sqrtf(0.5);
    float scale_half = 0.5;

    Vsma(source_l, scale_sqrt_half, destination, length());
    Vsma(source_r, scale_sqrt_half, destination, length());
    Vadd(source_c, destination, destination, length());
    Vsma(source_sl, scale_half, destination, length());
    Vsma(source_sr, scale_half, destination, length());
  } else if (number_of_source_channels == 4 &&
             number_of_destination_channels == 2) {
    // Down-mixing: 4 -> 2
    //   output.L = 0.5 * (input.L + input.SL)
    //   output.R = 0.5 * (input.R + input.SR)
    base::span<const float> source_l =
        source_bus.ChannelByType(kChannelLeft)->Span();
    base::span<const float> source_r =
        source_bus.ChannelByType(kChannelRight)->Span();
    base::span<const float> source_sl =
        source_bus.ChannelByType(kChannelSurroundLeft)->Span();
    base::span<const float> source_sr =
        source_bus.ChannelByType(kChannelSurroundRight)->Span();

    base::span<float> destination_l =
        ChannelByType(kChannelLeft)->MutableSpan();
    base::span<float> destination_r =
        ChannelByType(kChannelRight)->MutableSpan();
    float scale_half = 0.5;

    Vsma(source_l, scale_half, destination_l, length());
    Vsma(source_sl, scale_half, destination_l, length());
    Vsma(source_r, scale_half, destination_r, length());
    Vsma(source_sr, scale_half, destination_r, length());
  } else if (number_of_source_channels == 6 &&
             number_of_destination_channels == 2) {
    // Down-mixing: 5.1 -> 2
    //   output.L = input.L + sqrt(1/2) * (input.C + input.SL)
    //   output.R = input.R + sqrt(1/2) * (input.C + input.SR)
    base::span<const float> source_l =
        source_bus.ChannelByType(kChannelLeft)->Span();
    base::span<const float> source_r =
        source_bus.ChannelByType(kChannelRight)->Span();
    base::span<const float> source_c =
        source_bus.ChannelByType(kChannelCenter)->Span();
    base::span<const float> source_sl =
        source_bus.ChannelByType(kChannelSurroundLeft)->Span();
    base::span<const float> source_sr =
        source_bus.ChannelByType(kChannelSurroundRight)->Span();

    base::span<float> destination_l =
        ChannelByType(kChannelLeft)->MutableSpan();
    base::span<float> destination_r =
        ChannelByType(kChannelRight)->MutableSpan();
    float scale_sqrt_half = sqrtf(0.5);

    Vadd(source_l, destination_l, destination_l, length());
    Vsma(source_c, scale_sqrt_half, destination_l, length());
    Vsma(source_sl, scale_sqrt_half, destination_l, length());
    Vadd(source_r, destination_r, destination_r, length());
    Vsma(source_c, scale_sqrt_half, destination_r, length());
    Vsma(source_sr, scale_sqrt_half, destination_r, length());
  } else if (number_of_source_channels == 6 &&
             number_of_destination_channels == 4) {
    // Down-mixing: 5.1 -> 4
    //   output.L = input.L + sqrt(1/2) * input.C
    //   output.R = input.R + sqrt(1/2) * input.C
    //   output.SL = input.SL
    //   output.SR = input.SR
    base::span<const float> source_l =
        source_bus.ChannelByType(kChannelLeft)->Span();
    base::span<const float> source_r =
        source_bus.ChannelByType(kChannelRight)->Span();
    base::span<const float> source_c =
        source_bus.ChannelByType(kChannelCenter)->Span();

    base::span<float> destination_l =
        ChannelByType(kChannelLeft)->MutableSpan();
    base::span<float> destination_r =
        ChannelByType(kChannelRight)->MutableSpan();
    float scale_sqrt_half = sqrtf(0.5);

    Vadd(source_l, destination_l, destination_l, length());
    Vsma(source_c, scale_sqrt_half, destination_l, length());
    Vadd(source_r, destination_r, destination_r, length());
    Vsma(source_c, scale_sqrt_half, destination_r, length());
    Channel(2)->SumFrom(source_bus.Channel(4));
    Channel(3)->SumFrom(source_bus.Channel(5));
  } else {
    // All other cases, fall back to the discrete sum. This will perform
    // channel-wise sum until the destination channels run out.
    DiscreteSumFrom(source_bus);
  }
}

void AudioBus::CopyWithGainFrom(const AudioBus& source_bus, float gain) {
  if (!TopologyMatches(source_bus)) {
    NOTREACHED();
  }

  if (source_bus.IsSilent()) {
    Zero();
    return;
  }

  unsigned number_of_channels = NumberOfChannels();
  DCHECK_LE(number_of_channels, kMaxBusChannels);
  if (number_of_channels > kMaxBusChannels) {
    return;
  }

  // If it is copying from the same bus and no need to change gain, just return.
  if (this == &source_bus && gain == 1) {
    return;
  }

  std::array<base::span<const float>, kMaxBusChannels> sources;
  std::array<base::span<float>, kMaxBusChannels> destinations;

  for (unsigned i = 0; i < number_of_channels; ++i) {
    sources[i] = source_bus.Channel(i)->Span();
    destinations[i] = Channel(i)->MutableSpan();
  }

  unsigned frames_to_process = length();

  // Handle gains of 0 and 1 (exactly) specially.
  if (gain == 1) {
    for (unsigned channel_index = 0; channel_index < number_of_channels;
         ++channel_index) {
      destinations[channel_index]
          .first(frames_to_process)
          .copy_from(sources[channel_index].first(frames_to_process));
    }
  } else if (gain == 0) {
    for (unsigned channel_index = 0; channel_index < number_of_channels;
         ++channel_index) {
      std::ranges::fill(destinations[channel_index].first(frames_to_process),
                        0.0f);
    }
  } else {
    for (unsigned channel_index = 0; channel_index < number_of_channels;
         ++channel_index) {
      vector_math::Vsmul(sources[channel_index], gain,
                         destinations[channel_index], frames_to_process);
    }
  }
}

void AudioBus::CopyWithSampleAccurateGainValuesFrom(
    const AudioBus& source_bus,
    base::span<const float> gain_values) {
  // Make sure we're processing from the same type of bus.
  // We *are* able to process from mono -> stereo
  if (source_bus.NumberOfChannels() != 1 && !TopologyMatches(source_bus)) {
    NOTREACHED();
  }

  CHECK_LE(gain_values.size(), source_bus.length());

  if (source_bus.length() == gain_values.size() &&
      source_bus.length() == length() && source_bus.IsSilent()) {
    Zero();
    return;
  }

  // We handle both the 1 -> N and N -> N case here.
  base::span<const float> source = source_bus.Channel(0)->Span();
  for (unsigned channel_index = 0; channel_index < NumberOfChannels();
       ++channel_index) {
    if (source_bus.NumberOfChannels() == NumberOfChannels()) {
      source = source_bus.Channel(channel_index)->Span();
    }
    base::span<float> destination = Channel(channel_index)->MutableSpan();
    vector_math::Vmul(source, gain_values, destination,
                      base::checked_cast<uint32_t>(gain_values.size()));
  }
}

scoped_refptr<AudioBus> AudioBus::CreateBySampleRateConverting(
    const AudioBus* source_bus,
    bool mix_to_mono,
    double new_sample_rate) {
  scoped_refptr<AudioBus> audio_bus = TryCreateBySampleRateConverting(
      source_bus, mix_to_mono, new_sample_rate);
  CHECK(audio_bus);
  return audio_bus;
}

scoped_refptr<AudioBus> AudioBus::TryCreateBySampleRateConverting(
    const AudioBus* source_bus,
    bool mix_to_mono,
    double new_sample_rate) {
  if (!source_bus || !audio_utilities::IsValidAudioBufferSampleRate(
                         source_bus->SampleRate())) {
    return nullptr;
  }

  double source_sample_rate = source_bus->SampleRate();
  double destination_sample_rate = new_sample_rate;
  double sample_rate_ratio = source_sample_rate / destination_sample_rate;
  unsigned number_of_source_channels = source_bus->NumberOfChannels();

  if (number_of_source_channels == 1) {
    mix_to_mono = false;  // already mono
  }

  if (source_sample_rate == destination_sample_rate) {
    // No sample-rate conversion is necessary.
    if (mix_to_mono) {
      return AudioBus::TryCreateByMixingToMono(source_bus);
    }

    // Return exact copy.
    return AudioBus::CreateBufferFromRange(source_bus, 0, source_bus->length());
  }

  // Prevent overflow during extreme up-sampling (e.g., 100,000 samples from
  // 1Hz to 48kHz calculates to 4,800,000,000 frames). Validate upfront against
  // signed integer limits to guarantee fail-fast handling before attempting any
  // buffer allocations via TryCreate.
  double destination_length_double =
      static_cast<double>(source_bus->length()) / sample_rate_ratio;
  if (!base::IsValueInRangeForNumericType<int>(destination_length_double)) {
    return nullptr;
  }
  int destination_length = static_cast<int>(destination_length_double);

  if (source_bus->IsSilent()) {
    scoped_refptr<AudioBus> silent_bus =
        TryCreate(number_of_source_channels, destination_length);
    if (!silent_bus) {
      return nullptr;
    }
    silent_bus->SetSampleRate(new_sample_rate);
    return silent_bus;
  }

  // First, mix to mono (if necessary) then sample-rate convert.
  const AudioBus* resampler_source_bus;
  scoped_refptr<AudioBus> mixed_mono_bus;
  if (mix_to_mono) {
    mixed_mono_bus = AudioBus::TryCreateByMixingToMono(source_bus);
    if (!mixed_mono_bus) {
      return nullptr;
    }
    resampler_source_bus = mixed_mono_bus.get();
  } else {
    // Directly resample without down-mixing.
    resampler_source_bus = source_bus;
  }

  // Create destination bus with same number of channels.
  unsigned number_of_destination_channels =
      resampler_source_bus->NumberOfChannels();
  scoped_refptr<AudioBus> destination_bus =
      TryCreate(number_of_destination_channels, destination_length);
  if (!destination_bus) {
    return nullptr;
  }

  // Sample-rate convert each channel.
  for (unsigned i = 0; i < number_of_destination_channels; ++i) {
    base::span<const float> source = resampler_source_bus->Channel(i)->Span();
    // Use a request size of 96 to select a 32-tap kernel for backwards
    // compatibility.
    media::SincResampler resampler(
        sample_rate_ratio, /*request_frames=*/96,
        base::BindRepeating(
            [](base::span<const float>& src, base::span<float> dest) {
              size_t frames_to_copy = std::min(src.size(), dest.size());
              auto [dest_fill, dest_zero] = dest.split_at(frames_to_copy);
              dest_fill.copy_from(src.take_first(frames_to_copy));
              std::ranges::fill(dest_zero, 0.0f);
            },
            std::ref(source)));
    resampler.Resample(destination_bus->Channel(i)->MutableSpan());
  }

  destination_bus->ClearSilentFlag();
  destination_bus->SetSampleRate(new_sample_rate);
  return destination_bus;
}

scoped_refptr<AudioBus> AudioBus::TryCreateByMixingToMono(
    const AudioBus* source_bus) {
  if (source_bus->IsSilent()) {
    return TryCreate(1, source_bus->length());
  }

  switch (source_bus->NumberOfChannels()) {
    case 1:
      // Simply create an exact copy.
      return AudioBus::CreateBufferFromRange(source_bus, 0,
                                             source_bus->length());
    case 2: {
      uint32_t n = source_bus->length();
      scoped_refptr<AudioBus> destination_bus = TryCreate(1, n);
      if (!destination_bus) {
        return nullptr;
      }

      base::span<const float> source_l = source_bus->Channel(0)->Span();
      base::span<const float> source_r = source_bus->Channel(1)->Span();
      base::span<float> destination =
          destination_bus->Channel(0)->MutableSpan();

      // Do the mono mixdown.
      for (uint32_t i = 0; i < n; ++i) {
        destination[i] = (source_l[i] + source_r[i]) * 0.5f;
      }

      destination_bus->ClearSilentFlag();
      destination_bus->SetSampleRate(source_bus->SampleRate());
      return destination_bus;
    }
  }

  NOTREACHED();
}

bool AudioBus::IsSilent() const {
  return std::ranges::all_of(channels_, &AudioChannel::IsSilent);
}

void AudioBus::ClearSilentFlag() {
  for (AudioChannel& channel : channels_) {
    channel.ClearSilentFlag();
  }
}

scoped_refptr<AudioBus> DecodeAudioFileData(base::span<const char> data) {
  std::unique_ptr<WebAudioBus> out =
      Platform::Current()->DecodeAudioFileData(data);
  if (out) {
    return out->Release();
  }
  return nullptr;
}

scoped_refptr<AudioBus> AudioBus::GetDataResource(int resource_id,
                                                  float sample_rate) {
  const WebData& resource = Platform::Current()->GetDataResource(resource_id);
  if (resource.IsEmpty()) {
    return nullptr;
  }

  // Currently, the only client of this method is caching the result -- so
  // it's reasonable to (potentially) pay a one-time flat access cost.
  // If this becomes problematic, we'll have the refactor DecodeAudioFileData
  // to take WebData and use segmented access.
  SegmentedBuffer::DeprecatedFlatData flat_data(
      resource.operator scoped_refptr<SharedBuffer>().get());
  scoped_refptr<AudioBus> audio_bus = DecodeAudioFileData(flat_data);

  if (!audio_bus.get()) {
    return nullptr;
  }

  // If the bus is already at the requested sample-rate then return as is.
  if (audio_bus->SampleRate() == sample_rate) {
    return audio_bus;
  }

  return AudioBus::CreateBySampleRateConverting(audio_bus.get(), false,
                                                sample_rate);
}

scoped_refptr<AudioBus> AudioBus::CreateBusFromInMemoryAudioFile(
    base::span<const uint8_t> data,
    bool mix_to_mono,
    float sample_rate) {
  scoped_refptr<AudioBus> audio_bus = DecodeAudioFileData(base::as_chars(data));
  if (!audio_bus.get()) {
    return nullptr;
  }

  // If the bus needs no conversion then return as is.
  if ((!mix_to_mono || audio_bus->NumberOfChannels() == 1) &&
      audio_bus->SampleRate() == sample_rate) {
    return audio_bus;
  }

  return AudioBus::TryCreateBySampleRateConverting(audio_bus.get(), mix_to_mono,
                                                   sample_rate);
}

}  // namespace blink
