OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromecast/media/cma/backend/alsa/slew_volume.h" | 5 #include "chromecast/media/cma/backend/alsa/slew_volume.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "media/base/vector_math.h" | 10 #include "media/base/vector_math.h" |
11 | 11 |
12 namespace { | 12 namespace { |
13 | 13 |
14 // The time to slew from current volume to target volume. | 14 // The time to slew from current volume to target volume. |
15 const int kMaxSlewTimeMs = 100; | 15 const int kMaxSlewTimeMs = 100; |
16 const int kDefaultSampleRate = 44100; | 16 const int kDefaultSampleRate = 44100; |
17 } | 17 |
18 } // namespace | |
19 | |
20 // Used to create ProcessFMAC and ProcessFMUL. | |
21 // |BULK_OPERATOR| processes all remaining data. | |
22 // |SINGLE_OPERATOR| processes a single datum. | |
23 // |ZERO_OPERATOR| is run when |current_volume_| is 0 | |
24 // |UNITY_OPERATOR| is run when |current_volume_| is 1 | |
25 #define PROCESS_DATA(BULK_OPERATOR, SINGLE_OPERATOR, ZERO_OPERATOR, \ | |
kmackay
2017/05/05 00:13:30
Not exactly what I had in mind...
bshaya
2017/05/05 00:30:35
Done.
| |
26 UNITY_OPERATOR) \ | |
27 do { \ | |
28 DCHECK(src); \ | |
29 DCHECK(dest); \ | |
30 /* Ensure |src| and |dest| are 16-byte aligned. */ \ | |
31 DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(src) & \ | |
32 (::media::vector_math::kRequiredAlignment - 1)); \ | |
33 DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(dest) & \ | |
34 (::media::vector_math::kRequiredAlignment - 1)); \ | |
35 \ | |
36 if (!frames) { \ | |
37 return; \ | |
38 } \ | |
39 \ | |
40 interrupted_ = false; \ | |
41 if (repeat_transition) { \ | |
42 current_volume_ = last_starting_volume_; \ | |
43 } else { \ | |
44 last_starting_volume_ = current_volume_; \ | |
45 } \ | |
46 \ | |
47 if (current_volume_ == volume_scale_) { \ | |
48 if (current_volume_ == 0.0) { \ | |
49 ZERO_OPERATOR; \ | |
50 return; \ | |
51 } \ | |
52 if (current_volume_ == 1.0) { \ | |
53 UNITY_OPERATOR; \ | |
54 return; \ | |
55 } \ | |
56 BULK_OPERATOR; \ | |
57 return; \ | |
58 } \ | |
59 \ | |
60 if (current_volume_ < volume_scale_) { \ | |
61 do { \ | |
62 SINGLE_OPERATOR; \ | |
63 ++src; \ | |
64 ++dest; \ | |
65 --frames; \ | |
66 current_volume_ += max_slew_per_sample_; \ | |
67 } while (current_volume_ < volume_scale_ && frames); \ | |
68 current_volume_ = std::min(current_volume_, volume_scale_); \ | |
69 } else { /* current_volume_ > volume_scale_ */ \ | |
70 do { \ | |
71 SINGLE_OPERATOR; \ | |
72 ++src; \ | |
73 ++dest; \ | |
74 --frames; \ | |
75 current_volume_ -= max_slew_per_sample_; \ | |
76 } while (current_volume_ > volume_scale_ && frames); \ | |
77 current_volume_ = std::max(current_volume_, volume_scale_); \ | |
78 } \ | |
79 while (frames && (reinterpret_cast<uintptr_t>(src) & \ | |
80 (::media::vector_math::kRequiredAlignment - 1))) { \ | |
81 SINGLE_OPERATOR; \ | |
82 ++src; \ | |
83 ++dest; \ | |
84 --frames; \ | |
85 } \ | |
86 if (!frames) { \ | |
87 return; \ | |
88 } \ | |
89 BULK_OPERATOR; \ | |
90 } while (0); | |
91 | |
92 #define BULK_OPERATOR_FMAC \ | |
93 ::media::vector_math::FMAC(src, current_volume_, frames, dest) | |
94 #define SINGLE_OPERATOR_FMAC (*dest) += (*src) * current_volume_ | |
95 #define ZERO_OPERATOR_FMAC | |
96 #define UNITY_OPERATOR_FMAC BULK_OPERATOR_FMAC | |
97 | |
98 #define BULK_OPERATOR_FMUL \ | |
99 ::media::vector_math::FMUL(src, current_volume_, frames, dest) | |
100 #define SINGLE_OPERATOR_FMUL (*dest) = (*src) * current_volume_ | |
101 #define ZERO_OPERATOR_FMUL memset(dest, 0, frames * sizeof(*dest)); | |
102 #define UNITY_OPERATOR_FMUL \ | |
103 do { \ | |
104 if (src == dest) { \ | |
105 return; \ | |
106 } \ | |
107 std::memcpy(dest, src, frames * sizeof(*dest)); \ | |
108 } while (0); | |
18 | 109 |
19 namespace chromecast { | 110 namespace chromecast { |
20 namespace media { | 111 namespace media { |
21 | 112 |
22 SlewVolume::SlewVolume() : SlewVolume(kMaxSlewTimeMs) {} | 113 SlewVolume::SlewVolume() : SlewVolume(kMaxSlewTimeMs) {} |
23 | 114 |
24 SlewVolume::SlewVolume(int max_slew_time_ms) | 115 SlewVolume::SlewVolume(int max_slew_time_ms) |
25 : sample_rate_(kDefaultSampleRate), | 116 : sample_rate_(kDefaultSampleRate), |
26 max_slew_time_ms_(max_slew_time_ms), | 117 max_slew_time_ms_(max_slew_time_ms), |
27 max_slew_per_sample_(1000.0 / (max_slew_time_ms_ * sample_rate_)) { | 118 max_slew_per_sample_(1000.0 / (max_slew_time_ms_ * sample_rate_)) {} |
28 LOG(INFO) << "Creating a slew volume: " << max_slew_time_ms; | |
29 } | |
30 | 119 |
31 void SlewVolume::SetSampleRate(int sample_rate) { | 120 void SlewVolume::SetSampleRate(int sample_rate) { |
121 DCHECK_GT(sample_rate, 0); | |
122 | |
32 sample_rate_ = sample_rate; | 123 sample_rate_ = sample_rate; |
33 SetVolume(volume_scale_); | 124 SetVolume(volume_scale_); |
34 } | 125 } |
35 | 126 |
36 // Slew rate should be volume_to_slew / slew_time / sample_rate | 127 // Slew rate should be volume_to_slew / slew_time / sample_rate |
37 void SlewVolume::SetVolume(double volume_scale) { | 128 void SlewVolume::SetVolume(double volume_scale) { |
38 volume_scale_ = volume_scale; | 129 volume_scale_ = volume_scale; |
39 if (interrupted_) { | 130 if (interrupted_) { |
40 current_volume_ = volume_scale_; | 131 current_volume_ = volume_scale_; |
41 } | 132 } |
42 if (volume_scale_ > current_volume_) { | 133 if (volume_scale_ > current_volume_) { |
43 max_slew_per_sample_ = (volume_scale_ - current_volume_) * 1000.0 / | 134 max_slew_per_sample_ = (volume_scale_ - current_volume_) * 1000.0 / |
44 (max_slew_time_ms_ * sample_rate_); | 135 (max_slew_time_ms_ * sample_rate_); |
45 } else { | 136 } else { |
46 max_slew_per_sample_ = (current_volume_ - volume_scale_) * 1000.0 / | 137 max_slew_per_sample_ = (current_volume_ - volume_scale_) * 1000.0 / |
47 (max_slew_time_ms_ * sample_rate_); | 138 (max_slew_time_ms_ * sample_rate_); |
48 } | 139 } |
49 } | 140 } |
50 | 141 |
51 void SlewVolume::SetMaxSlewTimeMs(int max_slew_time_ms) { | 142 void SlewVolume::SetMaxSlewTimeMs(int max_slew_time_ms) { |
143 DCHECK_GE(max_slew_time_ms, 0); | |
144 | |
52 max_slew_time_ms_ = max_slew_time_ms; | 145 max_slew_time_ms_ = max_slew_time_ms; |
53 } | 146 } |
54 | 147 |
55 void SlewVolume::Interrupted() { | 148 void SlewVolume::Interrupted() { |
56 interrupted_ = true; | 149 interrupted_ = true; |
57 current_volume_ = volume_scale_; | 150 current_volume_ = volume_scale_; |
58 } | 151 } |
59 | 152 |
60 void SlewVolume::ProcessFMAC(bool repeat_transition, | 153 void SlewVolume::ProcessFMAC(bool repeat_transition, |
61 const float* src, | 154 const float* src, |
62 int frames, | 155 int frames, |
63 float* dest) { | 156 float* dest) { |
64 DCHECK(src); | 157 PROCESS_DATA(BULK_OPERATOR_FMAC, SINGLE_OPERATOR_FMAC, ZERO_OPERATOR_FMAC, |
65 DCHECK(dest); | 158 UNITY_OPERATOR_FMAC); |
66 // Ensure |src| and |dest| are 16-byte aligned. | |
67 DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(src) & | |
68 (::media::vector_math::kRequiredAlignment - 1)); | |
69 DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(dest) & | |
70 (::media::vector_math::kRequiredAlignment - 1)); | |
71 | |
72 if (!frames) { | |
73 return; | |
74 } | |
75 | |
76 interrupted_ = false; | |
77 if (repeat_transition) { | |
78 current_volume_ = last_starting_volume_; | |
79 } else { | |
80 last_starting_volume_ = current_volume_; | |
81 } | |
82 | |
83 if (current_volume_ == volume_scale_) { | |
84 if (current_volume_ == 0.0) { | |
85 return; | |
86 } | |
87 ::media::vector_math::FMAC(src, current_volume_, frames, dest); | |
88 return; | |
89 } else if (current_volume_ < volume_scale_) { | |
90 do { | |
91 (*dest) += (*src) * current_volume_; | |
92 ++src; | |
93 ++dest; | |
94 --frames; | |
95 current_volume_ += max_slew_per_sample_; | |
96 } while (current_volume_ < volume_scale_ && frames); | |
97 current_volume_ = std::min(current_volume_, volume_scale_); | |
98 } else { // current_volume_ > volume_scale_ | |
99 do { | |
100 (*dest) += (*src) * current_volume_; | |
101 ++src; | |
102 ++dest; | |
103 --frames; | |
104 current_volume_ -= max_slew_per_sample_; | |
105 } while (current_volume_ > volume_scale_ && frames); | |
106 current_volume_ = std::max(current_volume_, volume_scale_); | |
107 } | |
108 | |
109 if (frames) { | |
110 for (int f = 0; f < frames; ++f) { | |
111 dest[f] += src[f] * current_volume_; | |
112 } | |
113 } | |
114 } | 159 } |
115 | 160 |
116 // Scaling samples naively like this takes 0.2% of the CPU's time @ 44100hz | 161 void SlewVolume::ProcessFMUL(bool repeat_transition, |
117 // on pineapple. | 162 const float* src, |
118 // Assumes 2 channel audio. | 163 int frames, |
119 bool SlewVolume::ProcessInterleaved(int32_t* data, int frames) { | 164 float* dest) { |
120 DCHECK(data); | 165 PROCESS_DATA(BULK_OPERATOR_FMUL, SINGLE_OPERATOR_FMUL, ZERO_OPERATOR_FMUL, |
121 | 166 UNITY_OPERATOR_FMUL); |
122 if (!frames) { | |
123 return true; | |
124 } | |
125 | |
126 interrupted_ = false; | |
127 if (current_volume_ == volume_scale_) { | |
128 if (current_volume_ == 1.0) { | |
129 return true; | |
130 } | |
131 for (int i = 0; i < 2 * frames; ++i) { | |
132 data[i] *= current_volume_; | |
133 } | |
134 return true; | |
135 } else if (current_volume_ < volume_scale_) { | |
136 do { | |
137 (*data) *= current_volume_; | |
138 ++data; | |
139 (*data) *= current_volume_; | |
140 ++data; | |
141 --frames; | |
142 current_volume_ += max_slew_per_sample_; | |
143 } while (current_volume_ < volume_scale_ && frames); | |
144 current_volume_ = std::min(current_volume_, volume_scale_); | |
145 } else { | |
146 do { | |
147 (*data) *= current_volume_; | |
148 ++data; | |
149 (*data) *= current_volume_; | |
150 ++data; | |
151 --frames; | |
152 current_volume_ -= max_slew_per_sample_; | |
153 } while (current_volume_ > volume_scale_ && frames); | |
154 current_volume_ = std::max(current_volume_, volume_scale_); | |
155 } | |
156 | |
157 if (current_volume_ == 1.0) { | |
158 return true; | |
159 } | |
160 | |
161 if (current_volume_ == 0.0) { | |
162 std::fill_n(data, frames * 2, 0); | |
163 return true; | |
164 } | |
165 | |
166 for (int i = 0; i < 2 * frames; ++i) { | |
167 data[i] *= current_volume_; | |
168 } | |
169 return true; | |
170 } | 167 } |
171 | 168 |
172 } // namespace media | 169 } // namespace media |
173 } // namespace chromecast | 170 } // namespace chromecast |
OLD | NEW |