OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "media/base/audio_renderer_mixer.h" | 5 #include "media/base/audio_renderer_mixer.h" |
6 | 6 |
7 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) | |
8 #include <xmmintrin.h> | |
9 #endif | |
10 | |
7 #include "base/bind.h" | 11 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
13 #include "base/cpu.h" | |
9 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/memory/aligned_memory.h" | |
10 #include "media/audio/audio_util.h" | 16 #include "media/audio/audio_util.h" |
11 #include "media/base/limits.h" | 17 #include "media/base/limits.h" |
12 | 18 |
13 namespace media { | 19 namespace media { |
14 | 20 |
15 AudioRendererMixer::AudioRendererMixer( | 21 AudioRendererMixer::AudioRendererMixer( |
16 const AudioParameters& input_params, const AudioParameters& output_params, | 22 const AudioParameters& input_params, const AudioParameters& output_params, |
17 const scoped_refptr<AudioRendererSink>& sink) | 23 const scoped_refptr<AudioRendererSink>& sink) |
18 : audio_sink_(sink), | 24 : audio_sink_(sink), |
19 current_audio_delay_milliseconds_(0) { | 25 current_audio_delay_milliseconds_(0) { |
(...skipping 15 matching lines...) Expand all Loading... | |
35 audio_sink_->Initialize(output_params, this); | 41 audio_sink_->Initialize(output_params, this); |
36 audio_sink_->Start(); | 42 audio_sink_->Start(); |
37 } | 43 } |
38 | 44 |
39 AudioRendererMixer::~AudioRendererMixer() { | 45 AudioRendererMixer::~AudioRendererMixer() { |
40 // AudioRendererSinks must be stopped before being destructed. | 46 // AudioRendererSinks must be stopped before being destructed. |
41 audio_sink_->Stop(); | 47 audio_sink_->Stop(); |
42 | 48 |
43 // Clean up |mixer_input_audio_data_|. | 49 // Clean up |mixer_input_audio_data_|. |
44 for (size_t i = 0; i < mixer_input_audio_data_.size(); ++i) | 50 for (size_t i = 0; i < mixer_input_audio_data_.size(); ++i) |
45 delete [] mixer_input_audio_data_[i]; | 51 base::AlignedFree(mixer_input_audio_data_[i]); |
46 mixer_input_audio_data_.clear(); | 52 mixer_input_audio_data_.clear(); |
47 | 53 |
48 // Ensures that all mixer inputs have stopped themselves prior to destruction | 54 // Ensures that all mixer inputs have stopped themselves prior to destruction |
49 // and have called RemoveMixerInput(). | 55 // and have called RemoveMixerInput(). |
50 DCHECK_EQ(mixer_inputs_.size(), 0U); | 56 DCHECK_EQ(mixer_inputs_.size(), 0U); |
51 } | 57 } |
52 | 58 |
53 void AudioRendererMixer::AddMixerInput( | 59 void AudioRendererMixer::AddMixerInput( |
54 const scoped_refptr<AudioRendererMixerInput>& input) { | 60 const scoped_refptr<AudioRendererMixerInput>& input) { |
55 base::AutoLock auto_lock(mixer_inputs_lock_); | 61 base::AutoLock auto_lock(mixer_inputs_lock_); |
(...skipping 21 matching lines...) Expand all Loading... | |
77 return number_of_frames; | 83 return number_of_frames; |
78 } | 84 } |
79 | 85 |
80 void AudioRendererMixer::ProvideInput(const std::vector<float*>& audio_data, | 86 void AudioRendererMixer::ProvideInput(const std::vector<float*>& audio_data, |
81 int number_of_frames) { | 87 int number_of_frames) { |
82 base::AutoLock auto_lock(mixer_inputs_lock_); | 88 base::AutoLock auto_lock(mixer_inputs_lock_); |
83 | 89 |
84 // Allocate staging area for each mixer input's audio data on first call. We | 90 // Allocate staging area for each mixer input's audio data on first call. We |
85 // won't know how much to allocate until here because of resampling. | 91 // won't know how much to allocate until here because of resampling. |
86 if (mixer_input_audio_data_.size() == 0) { | 92 if (mixer_input_audio_data_.size() == 0) { |
87 // TODO(dalecurtis): If we switch to AVX/SSE optimization, we'll need to | |
88 // allocate these on 32-byte boundaries and ensure they're sized % 32 bytes. | |
89 mixer_input_audio_data_.reserve(audio_data.size()); | 93 mixer_input_audio_data_.reserve(audio_data.size()); |
90 for (size_t i = 0; i < audio_data.size(); ++i) | 94 for (size_t i = 0; i < audio_data.size(); ++i) { |
91 mixer_input_audio_data_.push_back(new float[number_of_frames]); | 95 // Allocate audio data with a 16-byte alignment for SSE optimizations. |
96 mixer_input_audio_data_.push_back(static_cast<float*>( | |
97 base::AlignedAlloc(sizeof(float) * number_of_frames, 16))); | |
98 } | |
92 mixer_input_audio_data_size_ = number_of_frames; | 99 mixer_input_audio_data_size_ = number_of_frames; |
93 } | 100 } |
94 | 101 |
95 // Sanity check our inputs. | 102 // Sanity check our inputs. |
96 DCHECK_LE(number_of_frames, mixer_input_audio_data_size_); | 103 DCHECK_LE(number_of_frames, mixer_input_audio_data_size_); |
97 DCHECK_EQ(audio_data.size(), mixer_input_audio_data_.size()); | 104 DCHECK_EQ(audio_data.size(), mixer_input_audio_data_.size()); |
98 | 105 |
99 // Zero |audio_data| so we're mixing into a clean buffer and return silence if | 106 // Zero |audio_data| so we're mixing into a clean buffer and return silence if |
100 // we couldn't get enough data from our inputs. | 107 // we couldn't get enough data from our inputs. |
101 for (size_t i = 0; i < audio_data.size(); ++i) | 108 for (size_t i = 0; i < audio_data.size(); ++i) |
(...skipping 11 matching lines...) Expand all Loading... | |
113 if (!input->playing()) | 120 if (!input->playing()) |
114 continue; | 121 continue; |
115 | 122 |
116 int frames_filled = input->callback()->Render( | 123 int frames_filled = input->callback()->Render( |
117 mixer_input_audio_data_, number_of_frames, | 124 mixer_input_audio_data_, number_of_frames, |
118 current_audio_delay_milliseconds_); | 125 current_audio_delay_milliseconds_); |
119 if (frames_filled == 0) | 126 if (frames_filled == 0) |
120 continue; | 127 continue; |
121 | 128 |
122 // Volume adjust and mix each mixer input into |audio_data| after rendering. | 129 // Volume adjust and mix each mixer input into |audio_data| after rendering. |
123 // TODO(dalecurtis): Optimize with NEON/SSE/AVX vector_fmac from FFmpeg. | |
124 for (size_t j = 0; j < audio_data.size(); ++j) { | 130 for (size_t j = 0; j < audio_data.size(); ++j) { |
125 float* dest = audio_data[j]; | 131 VectorFMAC( |
126 float* source = mixer_input_audio_data_[j]; | 132 mixer_input_audio_data_[j], volume, frames_filled, audio_data[j]); |
127 for (int k = 0; k < frames_filled; ++k) | |
128 dest[k] += source[k] * static_cast<float>(volume); | |
129 } | 133 } |
130 | 134 |
131 // No need to clamp values as InterleaveFloatToInt() will take care of this | 135 // No need to clamp values as InterleaveFloatToInt() will take care of this |
132 // for us later when data is transferred to the browser process. | 136 // for us later when data is transferred to the browser process. |
133 } | 137 } |
134 } | 138 } |
135 | 139 |
136 void AudioRendererMixer::OnRenderError() { | 140 void AudioRendererMixer::OnRenderError() { |
137 base::AutoLock auto_lock(mixer_inputs_lock_); | 141 base::AutoLock auto_lock(mixer_inputs_lock_); |
138 | 142 |
139 // Call each mixer input and signal an error. | 143 // Call each mixer input and signal an error. |
140 for (AudioRendererMixerInputSet::iterator it = mixer_inputs_.begin(); | 144 for (AudioRendererMixerInputSet::iterator it = mixer_inputs_.begin(); |
141 it != mixer_inputs_.end(); ++it) { | 145 it != mixer_inputs_.end(); ++it) { |
142 (*it)->callback()->OnRenderError(); | 146 (*it)->callback()->OnRenderError(); |
143 } | 147 } |
144 } | 148 } |
145 | 149 |
150 void AudioRendererMixer::VectorFMAC(const float* src, float scale, int len, | |
151 float* dest) { | |
152 // Rely on function level static initialization to keep VectorFMACProc | |
153 // selection thread safe. | |
154 typedef void (*VectorFMACProc)(const float* src, float scale, int len, | |
155 float* dest); | |
156 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) | |
157 static const VectorFMACProc kVectorFMACProc = | |
158 base::CPU().has_sse() ? VectorFMAC_SSE : VectorFMAC_C; | |
159 #else | |
160 static const VectorFMACProc kVectorFMACProc = VectorFMAC_C; | |
161 #endif | |
162 | |
163 return kVectorFMACProc(src, scale, len, dest); | |
164 } | |
165 | |
166 void AudioRendererMixer::VectorFMAC_C(const float* src, float scale, int len, | |
167 float* dest) { | |
168 for (int i = 0; i < len; ++i) | |
169 dest[i] += src[i] * scale; | |
170 } | |
171 | |
172 #if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) | |
173 void AudioRendererMixer::VectorFMAC_SSE(const float* src, float scale, int len, | |
174 float* dest) { | |
175 // Ensure |src| and |dest| are aligned properly. | |
Ami GONE FROM CHROMIUM
2012/07/27 17:33:11
Note an alternative is to handle a leading unalign
DaleCurtis
2012/07/27 21:23:42
Certainly not after all the trouble I've gone to g
| |
176 DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(src) & 0x0F); | |
177 DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(dest) & 0x0F); | |
178 | |
179 __m128 m_scale = _mm_set_ps1(scale); | |
180 int rem = len % 4; | |
181 for (int i = 0; i < len - rem; i += 4) { | |
182 _mm_store_ps(dest + i, _mm_add_ps(_mm_load_ps(dest + i), | |
183 _mm_mul_ps(_mm_load_ps(src + i), m_scale))); | |
184 } | |
185 | |
186 // Handle any remaining values that wouldn't fit in an SSE pass. | |
187 if (rem) | |
188 VectorFMAC_C(src + len - rem, scale, rem, dest + len - rem); | |
189 } | |
190 #endif | |
191 | |
146 } // namespace media | 192 } // namespace media |
OLD | NEW |