// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_OSCILLATOR_HANDLER_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_OSCILLATOR_HANDLER_H_

#include "base/containers/span.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_oscillator_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_oscillator_type.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/modules/webaudio/audio_param.h"
#include "third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.h"
#include "third_party/blink/renderer/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/heap/cross_thread_persistent.h"
#include "third_party/blink/renderer/platform/wtf/threading.h"

namespace blink {

class ExceptionState;
class PeriodicWaveImpl;

class MODULES_EXPORT OscillatorHandler final
    : public AudioScheduledSourceHandler {
  FRIEND_TEST_ALL_PREFIXES(OscillatorHandlerTest, RoundingBug);

 public:
  // The waveform type.
  // These must be defined as in the .idl file.
  enum : uint8_t {
    SINE = 0,
    SQUARE = 1,
    SAWTOOTH = 2,
    TRIANGLE = 3,
    CUSTOM = 4
  };

  // Breakpoints where we decide to do linear interpolation, 3-point
  // interpolation or 5-point interpolation.  See DoInterpolation().
  static constexpr float kInterpolate2Point = 0.3f;
  static constexpr float kInterpolate3Point = 0.16f;

  // Note: After calling Create, you must call SetInitialType() or
  // SetInitialPeriodicWave() to complete initialization and handle potential
  // allocation failures.
  static scoped_refptr<OscillatorHandler> Create(AudioNode&,
                                                 float sample_rate,
                                                 AudioParamHandler& frequency,
                                                 AudioParamHandler& detune);
  ~OscillatorHandler() override;

  // AudioHandler
  void Process(uint32_t frames_to_process) override;

  V8OscillatorType::Enum GetType() const;
  void SetType(V8OscillatorType::Enum, ExceptionState&);

  void SetPeriodicWave(PeriodicWaveImpl*);

  void HandleStoppableSourceNode() override;

  // Sets the initial type. Returns false if table allocation fails.
  bool SetInitialType(const String& oscillator_type);

  // Sets the initial periodic wave.
  void SetInitialPeriodicWave(PeriodicWaveImpl*);

 private:
  OscillatorHandler(AudioNode&,
                    float sample_rate,
                    AudioParamHandler& frequency,
                    AudioParamHandler& detune);

  // Returns true on success.
  bool SetType(uint8_t);

  // Returns true if there are sample-accurate timeline parameter changes.
  bool CalculateSampleAccuratePhaseIncrements(uint32_t frames_to_process);

  bool PropagatesSilence() const override;

  base::WeakPtr<AudioScheduledSourceHandler> AsWeakPtr() override;

  // Compute the output for k-rate AudioParams
  double ProcessKRate(int n,
                      base::span<float> destination,
                      double virtual_read_index) const;

  // Scalar version for the main loop in ProcessKRate().  Returns the updated
  // virtual_read_index.
  double ProcessKRateScalar(int start_index,
                            int n,
                            base::span<float> destination,
                            double virtual_read_index,
                            float frequency,
                            float rate_scale) const;

  // Vectorized version (if available) for the main loop in ProcessKRate().
  // Returns the number of elements processed and the updated
  // virtual_read_index.
  std::tuple<int, double> ProcessKRateVector(int n,
                                             base::span<float> destination,
                                             double virtual_read_index,
                                             float frequency,
                                             float rate_scale) const;

  // Compute the output for a-rate AudioParams
  double ProcessARate(int n,
                      base::span<float> destination,
                      double virtual_read_index,
                      base::span<float> phase_increments) const;

  // Scalar version of ProcessARate().  Also handles any values not handled by
  // the vector version.
  //
  //   k
  //     start index for where to write the result (and read phase_increments)
  //   n
  //     total number of frames to process
  //   destination
  //     Array where the samples values are written
  //   virtual_read_index
  //     index into the wave data tables containing the waveform
  //   phase_increments
  //     phase change to use for each frame of output
  //
  // Returns the updated virtual_read_index.
  double ProcessARateScalar(int k,
                            int n,
                            base::span<float> destination,
                            double virtual_read_index,
                            base::span<const float> phase_increments) const;

  // Vector version of ProcessARate().  Returns the number of frames processed
  // and the update virtual_read_index.
  std::tuple<int, double> ProcessARateVector(
      int n,
      base::span<float> destination,
      double virtual_read_index,
      base::span<const float> phase_increments) const;

  // Handles the linear interpolation in ProcessARateVector().
  //
  //   destination
  //     Where the interpolated values are written.
  //   virtual_read_index
  //     index into the wave table data
  //   phase_increments
  //     phase increments array
  //   periodic_wave_size
  //     Length of the periodic wave stored in the wave tables
  //   lower_wave_data
  //     Array of the 4 lower wave table arrays
  //   higher_wave_data
  //     Array of the 4 higher wave table arrays
  //   table_interpolation_factor
  //     Array of linear interpolation factors to use between the lower and
  //     higher wave tables.
  //
  // Returns the updated virtual_read_index
  double ProcessARateVectorKernel(
      base::span<float> destination,
      double virtual_read_index,
      base::span<const float> phase_increments,
      unsigned periodic_wave_size,
      const std::array<base::span<const float>, 4>& lower_wave_data,
      const std::array<base::span<const float>, 4>& higher_wave_data,
      const std::array<float, 4>& table_interpolation_factor) const;

  // One of the waveform types defined in the enum.
  uint8_t type_;

  // Frequency value in Hertz.
  scoped_refptr<AudioParamHandler> frequency_;

  // Detune value (deviating from the frequency) in Cents.
  scoped_refptr<AudioParamHandler> detune_;

  bool first_render_ = true;

  // m_virtualReadIndex is a sample-frame index into our buffer representing the
  // current playback position.  Since it's floating-point, it has sub-sample
  // accuracy.
  double virtual_read_index_ = 0;

  // Stores sample-accurate values calculated according to frequency and detune.
  AudioFloatArray phase_increments_;
  AudioFloatArray detune_values_;

  // PeriodicWaveImpl cannot cause cycles with OscillatorNode as it is not
  // scriptable.
  CrossThreadPersistent<PeriodicWaveImpl> periodic_wave_;

  base::WeakPtrFactory<AudioScheduledSourceHandler> weak_ptr_factory_{this};
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_OSCILLATOR_HANDLER_H_
