text: // Copyright 2020 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_RENDERER_H_
  #define SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_RENDERER_H_
  
  #include <lib/fidl/cpp/binding_set.h>
  
  #include <mutex>
  #include <optional>
  
  #include "src/lib/fxl/synchronization/thread_annotations.h"
  #include "src/media/audio/audio_core/base_renderer.h"
  #include "src/media/audio/audio_core/stream_usage.h"
  #include "src/media/audio/audio_core/stream_volume_manager.h"
  
  namespace media::audio {
  
  // Smoothly change gain from its current value to end_gain_db, over the specified duration.
  struct GainRamp {
    // The target gain for this ramp, in decibels.
    float end_gain_db;
    zx::duration duration;
    fuchsia::media::audio::RampType ramp_type;
  };
  
  // A command to realize gain changes (potentially multiple in tight succession) on connected links.
  struct StreamGainCommand {
    // Gain to be set immediately, in decibels.
    std::optional<float> gain_db;
    // A ramp with which to apply a subsequent gain change, after setting the 'gain_db' above.
    std::optional<GainRamp> ramp;
    // Independent of gain_db or ramping, is this stream muted.
    std::optional<bool> mute;
  };
  
  class AudioRenderer : public BaseRenderer,
                        public fuchsia::media::audio::GainControl,
                        public StreamVolume {
   public:
    static std::shared_ptr<AudioRenderer> Create(
        fidl::InterfaceRequest<fuchsia::media::AudioRenderer> audio_renderer_request,
        Context* context) {
      return std::make_shared<AudioRenderer>(std::move(audio_renderer_request), context);
    }
  
    // Callers should use the |Create| method instead, this is only public to enable std::make_shared.
    AudioRenderer(fidl::InterfaceRequest<fuchsia::media::AudioRenderer> audio_renderer_request,
                  Context* context);
    ~AudioRenderer() override;
  
   private:
    // |media::audio::AudioObject|
    std::optional<Format> format() const final { return format_; }
    std::optional<StreamUsage> usage() const override {
      return {StreamUsage::WithRenderUsage(usage_)};
    }
    void OnLinkAdded() override;
  
    // |fuchsia::media::AudioRenderer|
    void SetReferenceClock(zx::clock ref_clock) final;
    void SetPcmStreamType(fuchsia::media::AudioStreamType format) final;
    void SetUsage(fuchsia::media::AudioRenderUsage usage) override;
    void BindGainControl(fidl::InterfaceRequest<fuchsia::media::audio::GainControl> request) final;
  
    // |media::audio::BaseRenderer|
    void ReportStart() final;
    void ReportStop() final;
    // Needed for ramped Play/Pause transitions
    void AddPayloadBufferInternal(uint32_t id, zx::vmo payload_buffer) final;
    void RemovePayloadBufferInternal(uint32_t id) final;
    void SendPacketInternal(fuchsia::media::StreamPacket packet, SendPacketCallback callback) final;
    void DiscardAllPacketsInternal(DiscardAllPacketsCallback callback) final;
    void EnableMinLeadTimeEventsInternal(bool enabled) final;
    void GetMinLeadTimeInternal(GetMinLeadTimeCallback callback) final;
    void PlayInternal(zx::time reference_time, zx::time media_time, PlayCallback callback) final;
    void PauseInternal(PauseCallback callback) final;
  
    // |media::audio::StreamVolume|
    fuchsia::media::Usage GetStreamUsage() const final;
    void RealizeVolume(VolumeCommand volume_command) final;
  
    // |fuchsia::media::audio::GainControl|
    void SetGain(float gain_db) final;
    void SetGainWithRamp(float gain_db, int64_t duration_ns,
                         fuchsia::media::audio::RampType ramp_type) final;
    void SetMute(bool muted) final;
    void NotifyGainMuteChanged();
    // TODO(mpuryear): Notify on SetGainWithRamp.
  
    void SetGainInternal(float gain_db);
    void SetGainWithRampInternal(float gain_db, int64_t duration_ns,
                                 fuchsia::media::audio::RampType ramp_type);
    void SetMuteInternal(bool muted);
  
    void SerializeWithPause(fit::closure callback);
    void PostStreamGainMute(StreamGainCommand gain_command);
  
    float stream_gain_db_ = 0.0f;
    bool mute_ = false;
    std::optional<Format> format_;
  
    fuchsia::media::AudioRenderUsage usage_ = fuchsia::media::AudioRenderUsage::MEDIA;
  
    // Set when pause is ramping, cleared when the ramp is finished.
    // Must be accessed on the FIDL thread only.
    struct PauseRampState {
      float prior_stream_gain_db;            // gain to restore after the pause ramp is complete
      std::vector<PauseCallback> callbacks;  // Pause calls in flight
      std::vector<fit::closure> queued;      // closures to run on completion
    };
    std::shared_ptr<PauseRampState> pause_ramp_state_;
    void FinishPauseRamp(std::shared_ptr<PauseRampState> expected_state);
  
    std::mutex mutex_;
    bool reference_clock_is_set_ FXL_GUARDED_BY(mutex_) = false;
    std::optional<float> notified_gain_db_ FXL_GUARDED_BY(mutex_);
    std::optional<bool> notified_mute_ FXL_GUARDED_BY(mutex_);
  
    class GainControlBinding : public fuchsia::media::audio::GainControl {
     public:
      static std::unique_ptr<GainControlBinding> Create(AudioRenderer* owner) {
        return std::unique_ptr<GainControlBinding>(new GainControlBinding(owner));
      }
  
      // |fuchsia::media::audio::GainControl|
      void SetGain(float gain_db) final;
      void SetGainWithRamp(float gain_db, int64_t duration_ns,
                           fuchsia::media::audio::RampType ramp_type) final;
      void SetMute(bool muted) final;
  
     private:
      friend class std::default_delete<GainControlBinding>;
  
      GainControlBinding(AudioRenderer* owner) : owner_(owner) {}
      ~GainControlBinding() override {}
  
      AudioRenderer* owner_;
    };
  
    friend class GainControlBinding;
  
    fidl::BindingSet<fuchsia::media::audio::GainControl, std::unique_ptr<GainControlBinding>>
        gain_control_bindings_;
  };
  
  }  // namespace media::audio
  
  #endif  // SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_RENDERER_H_
  