1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromecast/media/base/slew_volume.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <cstring>
10 
11 #include "base/check_op.h"
12 #include "media/base/vector_math.h"
13 
14 namespace {
15 
16 // The time to slew from current volume to target volume.
17 const int kMaxSlewTimeMs = 100;
18 const int kDefaultSampleRate = 44100;
19 
20 }  // namespace
21 
22 struct FMACTraits {
ProcessBulkDataFMACTraits23   static void ProcessBulkData(const float* src,
24                               float volume,
25                               int frames,
26                               float* dest) {
27     ::media::vector_math::FMAC(src, volume, frames, dest);
28   }
29 
ProcessSingleDatumFMACTraits30   static void ProcessSingleDatum(const float* src, float volume, float* dest) {
31     (*dest) += (*src) * volume;
32   }
33 
ProcessZeroVolumeFMACTraits34   static void ProcessZeroVolume(const float* src, int frames, float* dest) {}
35 
ProcessUnityVolumeFMACTraits36   static void ProcessUnityVolume(const float* src, int frames, float* dest) {
37     ProcessBulkData(src, 1.0, frames, dest);
38   }
39 };
40 
41 struct FMULTraits {
ProcessBulkDataFMULTraits42   static void ProcessBulkData(const float* src,
43                               float volume,
44                               int frames,
45                               float* dest) {
46     ::media::vector_math::FMUL(src, volume, frames, dest);
47   }
48 
ProcessSingleDatumFMULTraits49   static void ProcessSingleDatum(const float* src, float volume, float* dest) {
50     (*dest) = (*src) * volume;
51   }
52 
ProcessZeroVolumeFMULTraits53   static void ProcessZeroVolume(const float* src, int frames, float* dest) {
54     std::memset(dest, 0, frames * sizeof(*dest));
55   }
56 
ProcessUnityVolumeFMULTraits57   static void ProcessUnityVolume(const float* src, int frames, float* dest) {
58     if (src == dest) {
59       return;
60     }
61     std::memcpy(dest, src, frames * sizeof(*dest));
62   }
63 };
64 
65 namespace chromecast {
66 namespace media {
67 
SlewVolume()68 SlewVolume::SlewVolume() : SlewVolume(kMaxSlewTimeMs) {}
69 
SlewVolume(int max_slew_time_ms)70 SlewVolume::SlewVolume(int max_slew_time_ms)
71     : SlewVolume(max_slew_time_ms, false) {}
72 
SlewVolume(int max_slew_time_ms,bool use_cosine_slew)73 SlewVolume::SlewVolume(int max_slew_time_ms, bool use_cosine_slew)
74     : sample_rate_(kDefaultSampleRate),
75       max_slew_time_ms_(max_slew_time_ms),
76       max_slew_per_sample_(1000.0 / (max_slew_time_ms_ * sample_rate_)),
77       use_cosine_slew_(use_cosine_slew) {}
78 
SetSampleRate(int sample_rate)79 void SlewVolume::SetSampleRate(int sample_rate) {
80   CHECK_GT(sample_rate, 0);
81 
82   sample_rate_ = sample_rate;
83   SetVolume(volume_scale_);
84 }
85 
SetVolume(double volume_scale)86 void SlewVolume::SetVolume(double volume_scale) {
87   volume_scale_ = volume_scale;
88   if (interrupted_) {
89     current_volume_ = volume_scale_;
90     last_starting_volume_ = current_volume_;
91   }
92 
93   // Slew rate should be volume_to_slew / slew_time / sample_rate, but use a
94   // minimum volume_to_slew of 0.1 to avoid very small slew per sample.
95   double volume_diff =
96       std::max(0.1, std::fabs(volume_scale_ - current_volume_));
97   max_slew_per_sample_ =
98       volume_diff * 1000.0 / (max_slew_time_ms_ * sample_rate_);
99 
100   if (use_cosine_slew_) {
101     // Set initial state for cosine slew. Cosine fading always lasts
102     // max_slew_time_ms_.
103     slew_counter_ = max_slew_time_ms_ * 0.001 * sample_rate_;
104     slew_angle_ = sin(M_PI / slew_counter_);
105     slew_offset_ = (current_volume_ + volume_scale_) * 0.5;
106     slew_cos_ = (current_volume_ - volume_scale_) * 0.5;
107     slew_sin_ = 0.0;
108   }
109 }
110 
LastBufferMaxMultiplier()111 float SlewVolume::LastBufferMaxMultiplier() {
112   return std::max(current_volume_, last_starting_volume_);
113 }
114 
SetMaxSlewTimeMs(int max_slew_time_ms)115 void SlewVolume::SetMaxSlewTimeMs(int max_slew_time_ms) {
116   CHECK_GE(max_slew_time_ms, 0);
117 
118   max_slew_time_ms_ = max_slew_time_ms;
119 }
120 
Interrupted()121 void SlewVolume::Interrupted() {
122   interrupted_ = true;
123   current_volume_ = volume_scale_;
124 }
125 
ProcessFMAC(bool repeat_transition,const float * src,int frames,int channels,float * dest)126 void SlewVolume::ProcessFMAC(bool repeat_transition,
127                              const float* src,
128                              int frames,
129                              int channels,
130                              float* dest) {
131   ProcessData<FMACTraits>(repeat_transition, src, frames, channels, dest);
132 }
133 
ProcessFMUL(bool repeat_transition,const float * src,int frames,int channels,float * dest)134 void SlewVolume::ProcessFMUL(bool repeat_transition,
135                              const float* src,
136                              int frames,
137                              int channels,
138                              float* dest) {
139   ProcessData<FMULTraits>(repeat_transition, src, frames, channels, dest);
140 }
141 
142 template <typename Traits>
ProcessData(bool repeat_transition,const float * src,int frames,int channels,float * dest)143 void SlewVolume::ProcessData(bool repeat_transition,
144                              const float* src,
145                              int frames,
146                              int channels,
147                              float* dest) {
148   DCHECK(src);
149   DCHECK(dest);
150   // Ensure |src| and |dest| are 16-byte aligned.
151   DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(src) &
152                     (::media::vector_math::kRequiredAlignment - 1));
153   DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(dest) &
154                     (::media::vector_math::kRequiredAlignment - 1));
155 
156   if (!frames) {
157     return;
158   }
159 
160   interrupted_ = false;
161   if (repeat_transition) {
162     current_volume_ = last_starting_volume_;
163   } else {
164     last_starting_volume_ = current_volume_;
165   }
166 
167   if (current_volume_ == volume_scale_) {
168     if (current_volume_ == 0.0) {
169       Traits::ProcessZeroVolume(src, frames * channels, dest);
170       return;
171     }
172     if (current_volume_ == 1.0) {
173       Traits::ProcessUnityVolume(src, frames * channels, dest);
174       return;
175     }
176     Traits::ProcessBulkData(src, current_volume_, frames * channels, dest);
177     return;
178   }
179 
180   if (use_cosine_slew_) {
181     int slew_frames = std::min(slew_counter_, frames);
182     frames -= slew_frames;
183     slew_counter_ -= slew_frames;
184     for (; slew_frames > 0; --slew_frames) {
185       slew_cos_ -= slew_sin_ * slew_angle_;
186       slew_sin_ += slew_cos_ * slew_angle_;
187       current_volume_ = std::min(1.0, std::max(0.0, slew_offset_ + slew_cos_));
188       for (int i = 0; i < channels; ++i) {
189         Traits::ProcessSingleDatum(src, current_volume_, dest);
190         ++src;
191         ++dest;
192       }
193     }
194     if (!slew_counter_) {
195       current_volume_ = volume_scale_;
196     }
197   } else if (current_volume_ < volume_scale_) {
198     do {
199       for (int i = 0; i < channels; ++i) {
200         Traits::ProcessSingleDatum(src, current_volume_, dest);
201         ++src;
202         ++dest;
203       }
204       --frames;
205       current_volume_ += max_slew_per_sample_;
206     } while (current_volume_ < volume_scale_ && frames);
207     current_volume_ = std::min(current_volume_, volume_scale_);
208   } else {  // current_volume_ > volume_scale_
209     do {
210       for (int i = 0; i < channels; ++i) {
211         Traits::ProcessSingleDatum(src, current_volume_, dest);
212         ++src;
213         ++dest;
214       }
215       --frames;
216       current_volume_ -= max_slew_per_sample_;
217     } while (current_volume_ > volume_scale_ && frames);
218     current_volume_ = std::max(current_volume_, volume_scale_);
219   }
220   while (frames && (reinterpret_cast<uintptr_t>(src) &
221                     (::media::vector_math::kRequiredAlignment - 1))) {
222     for (int i = 0; i < channels; ++i) {
223       Traits::ProcessSingleDatum(src, current_volume_, dest);
224       ++src;
225       ++dest;
226     }
227     --frames;
228   }
229   if (!frames) {
230     return;
231   }
232   Traits::ProcessBulkData(src, current_volume_, frames * channels, dest);
233 }
234 
235 }  // namespace media
236 }  // namespace chromecast
237