blob: 7ef9ff201e74f99ed8d7b6c6bea6d42dce1e5332 [file] [log] [blame]
Glenn Kasten99e53b82012-01-19 08:59:58 -08001/*
Mathias Agopian65ab4712010-07-14 17:59:35 -07002**
3** Copyright 2007, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#define LOG_TAG "AudioMixer"
Glenn Kasten7f5d3352013-02-15 23:55:04 +000019//#define LOG_NDEBUG 0
Mathias Agopian65ab4712010-07-14 17:59:35 -070020
Mikhail Naganov3b73e992019-07-31 14:53:29 -070021#include <sstream>
Mathias Agopian65ab4712010-07-14 17:59:35 -070022#include <stdint.h>
23#include <string.h>
24#include <stdlib.h>
Andy Hung5e58b0a2014-06-23 19:07:29 -070025#include <math.h>
Mathias Agopian65ab4712010-07-14 17:59:35 -070026#include <sys/types.h>
27
28#include <utils/Errors.h>
29#include <utils/Log.h>
30
Jean-Michel Trivi0d255b22011-05-24 15:53:33 -070031#include <system/audio.h>
32
Glenn Kasten3b21c502011-12-15 09:52:39 -080033#include <audio_utils/primitives.h>
Andy Hungef7c7fb2014-05-12 16:51:41 -070034#include <audio_utils/format.h>
Andy Hung068561c2017-01-03 17:09:32 -080035#include <media/AudioMixer.h>
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -070036
Andy Hung296b7412014-06-17 15:25:47 -070037#include "AudioMixerOps.h"
Mathias Agopian65ab4712010-07-14 17:59:35 -070038
Andy Hunge93b6b72014-07-17 21:30:53 -070039// The FCC_2 macro refers to the Fixed Channel Count of 2 for the legacy integer mixer.
Andy Hung296b7412014-06-17 15:25:47 -070040#ifndef FCC_2
41#define FCC_2 2
42#endif
43
Andy Hunge93b6b72014-07-17 21:30:53 -070044// Look for MONO_HACK for any Mono hack involving legacy mono channel to
45// stereo channel conversion.
46
Andy Hung296b7412014-06-17 15:25:47 -070047/* VERY_VERY_VERBOSE_LOGGING will show exactly which process hook and track hook is
48 * being used. This is a considerable amount of log spam, so don't enable unless you
49 * are verifying the hook based code.
50 */
51//#define VERY_VERY_VERBOSE_LOGGING
52#ifdef VERY_VERY_VERBOSE_LOGGING
53#define ALOGVV ALOGV
54//define ALOGVV printf // for test-mixer.cpp
55#else
56#define ALOGVV(a...) do { } while (0)
57#endif
58
Andy Hung1b2fdcb2014-07-16 17:44:34 -070059// Set to default copy buffer size in frames for input processing.
Mikhail Naganov3b73e992019-07-31 14:53:29 -070060static constexpr size_t kCopyBufferFrameCount = 256;
Andy Hung1b2fdcb2014-07-16 17:44:34 -070061
Mathias Agopian65ab4712010-07-14 17:59:35 -070062namespace android {
Mathias Agopian65ab4712010-07-14 17:59:35 -070063
64// ----------------------------------------------------------------------------
Andy Hung1b2fdcb2014-07-16 17:44:34 -070065
Mikhail Naganov7ad7a252019-07-30 14:42:32 -070066bool AudioMixer::isValidChannelMask(audio_channel_mask_t channelMask) const {
67 return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible.
Glenn Kastenc5ac4cb2011-12-12 09:05:55 -080068}
Mathias Agopian65ab4712010-07-14 17:59:35 -070069
Andy Hunge93b6b72014-07-17 21:30:53 -070070// Called when channel masks have changed for a track name
Andy Hung7f475492014-08-25 16:36:37 -070071// TODO: Fix DownmixerBufferProvider not to (possibly) change mixer input format,
Andy Hunge93b6b72014-07-17 21:30:53 -070072// which will simplify this logic.
73bool AudioMixer::setChannelMasks(int name,
74 audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) {
Andy Hung1bc088a2018-02-09 15:57:31 -080075 LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
Mikhail Naganov7ad7a252019-07-30 14:42:32 -070076 const std::shared_ptr<Track> &track = getTrack(name);
Andy Hunge93b6b72014-07-17 21:30:53 -070077
jiabin245cdd92018-12-07 17:55:15 -080078 if (trackChannelMask == (track->channelMask | track->mHapticChannelMask)
79 && mixerChannelMask == (track->mMixerChannelMask | track->mMixerHapticChannelMask)) {
Andy Hunge93b6b72014-07-17 21:30:53 -070080 return false; // no need to change
Jean-Michel Trivi9bd23222012-04-16 13:43:48 -070081 }
Mikhail Naganov55773032020-10-01 15:08:13 -070082 const audio_channel_mask_t hapticChannelMask =
83 static_cast<audio_channel_mask_t>(trackChannelMask & AUDIO_CHANNEL_HAPTIC_ALL);
84 trackChannelMask = static_cast<audio_channel_mask_t>(
85 trackChannelMask & ~AUDIO_CHANNEL_HAPTIC_ALL);
86 const audio_channel_mask_t mixerHapticChannelMask = static_cast<audio_channel_mask_t>(
87 mixerChannelMask & AUDIO_CHANNEL_HAPTIC_ALL);
88 mixerChannelMask = static_cast<audio_channel_mask_t>(
89 mixerChannelMask & ~AUDIO_CHANNEL_HAPTIC_ALL);
Andy Hunge93b6b72014-07-17 21:30:53 -070090 // always recompute for both channel masks even if only one has changed.
91 const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask);
92 const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask);
jiabin245cdd92018-12-07 17:55:15 -080093 const uint32_t hapticChannelCount = audio_channel_count_from_out_mask(hapticChannelMask);
94 const uint32_t mixerHapticChannelCount =
95 audio_channel_count_from_out_mask(mixerHapticChannelMask);
Andy Hunge93b6b72014-07-17 21:30:53 -070096
97 ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX)
98 && trackChannelCount
99 && mixerChannelCount);
Andy Hung8ed196a2018-01-05 13:21:11 -0800100 track->channelMask = trackChannelMask;
101 track->channelCount = trackChannelCount;
102 track->mMixerChannelMask = mixerChannelMask;
103 track->mMixerChannelCount = mixerChannelCount;
jiabin245cdd92018-12-07 17:55:15 -0800104 track->mHapticChannelMask = hapticChannelMask;
105 track->mHapticChannelCount = hapticChannelCount;
106 track->mMixerHapticChannelMask = mixerHapticChannelMask;
107 track->mMixerHapticChannelCount = mixerHapticChannelCount;
108
109 if (track->mHapticChannelCount > 0) {
110 track->mAdjustInChannelCount = track->channelCount + track->mHapticChannelCount;
jiabin0e6babc2021-10-21 20:35:05 +0000111 track->mAdjustOutChannelCount = track->channelCount;
jiabin245cdd92018-12-07 17:55:15 -0800112 track->mKeepContractedChannels = track->mHapticPlaybackEnabled;
113 } else {
114 track->mAdjustInChannelCount = 0;
115 track->mAdjustOutChannelCount = 0;
jiabin245cdd92018-12-07 17:55:15 -0800116 track->mKeepContractedChannels = false;
117 }
Andy Hunge93b6b72014-07-17 21:30:53 -0700118
jiabin76d94692022-12-15 21:51:21 +0000119 track->mInputFrameSize = audio_bytes_per_frame(
120 track->channelCount + track->mHapticChannelCount, track->mFormat);
121
Andy Hunge93b6b72014-07-17 21:30:53 -0700122 // channel masks have changed, does this track need a downmixer?
123 // update to try using our desired format (if we aren't already using it)
Andy Hung8ed196a2018-01-05 13:21:11 -0800124 const status_t status = track->prepareForDownmix();
Andy Hunge93b6b72014-07-17 21:30:53 -0700125 ALOGE_IF(status != OK,
Andy Hung0f451e92014-08-04 21:28:47 -0700126 "prepareForDownmix error %d, track channel mask %#x, mixer channel mask %#x",
Andy Hung8ed196a2018-01-05 13:21:11 -0800127 status, track->channelMask, track->mMixerChannelMask);
Andy Hunge93b6b72014-07-17 21:30:53 -0700128
Yung Ti Su1a0ecc32018-05-07 11:09:15 +0800129 // always do reformat since channel mask changed,
130 // do it after downmix since track format may change!
131 track->prepareForReformat();
Andy Hunge93b6b72014-07-17 21:30:53 -0700132
jiabin0e6babc2021-10-21 20:35:05 +0000133 track->prepareForAdjustChannels(mFrameCount);
jiabindce8f8c2018-12-10 17:49:31 -0800134
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700135 // Resampler channels may have changed.
136 track->recreateResampler(mSampleRate);
Andy Hunge93b6b72014-07-17 21:30:53 -0700137 return true;
138}
139
Andy Hung8ed196a2018-01-05 13:21:11 -0800140void AudioMixer::Track::unprepareForDownmix() {
Andy Hung0f451e92014-08-04 21:28:47 -0700141 ALOGV("AudioMixer::unprepareForDownmix(%p)", this);
Jean-Michel Trivi9bd23222012-04-16 13:43:48 -0700142
Andy Hung8ed196a2018-01-05 13:21:11 -0800143 if (mPostDownmixReformatBufferProvider.get() != nullptr) {
Andy Hung85395892017-04-25 16:47:52 -0700144 // release any buffers held by the mPostDownmixReformatBufferProvider
Andy Hung8ed196a2018-01-05 13:21:11 -0800145 // before deallocating the mDownmixerBufferProvider.
Andy Hung85395892017-04-25 16:47:52 -0700146 mPostDownmixReformatBufferProvider->reset();
147 }
148
Andy Hung7f475492014-08-25 16:36:37 -0700149 mDownmixRequiresFormat = AUDIO_FORMAT_INVALID;
Andy Hung8ed196a2018-01-05 13:21:11 -0800150 if (mDownmixerBufferProvider.get() != nullptr) {
Jean-Michel Trivi9bd23222012-04-16 13:43:48 -0700151 // this track had previously been configured with a downmixer, delete it
Andy Hung8ed196a2018-01-05 13:21:11 -0800152 mDownmixerBufferProvider.reset(nullptr);
Andy Hung0f451e92014-08-04 21:28:47 -0700153 reconfigureBufferProviders();
Jean-Michel Trivi9bd23222012-04-16 13:43:48 -0700154 } else {
155 ALOGV(" nothing to do, no downmixer to delete");
156 }
157}
158
Andy Hung8ed196a2018-01-05 13:21:11 -0800159status_t AudioMixer::Track::prepareForDownmix()
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700160{
Andy Hung0f451e92014-08-04 21:28:47 -0700161 ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x",
162 this, channelMask);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700163
Jean-Michel Trivi9bd23222012-04-16 13:43:48 -0700164 // discard the previous downmixer if there was one
Andy Hung0f451e92014-08-04 21:28:47 -0700165 unprepareForDownmix();
Andy Hung73e62e22015-04-20 12:06:38 -0700166 // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800167 // are not the same and not handled internally, as mono for channel position masks is.
Andy Hung0f451e92014-08-04 21:28:47 -0700168 if (channelMask == mMixerChannelMask
169 || (channelMask == AUDIO_CHANNEL_OUT_MONO
Judy Hsiaoc5cf9e22019-08-15 11:32:02 +0800170 && isAudioChannelPositionMask(mMixerChannelMask))) {
Andy Hung0f451e92014-08-04 21:28:47 -0700171 return NO_ERROR;
172 }
Andy Hung650ceb92015-01-29 13:31:12 -0800173 // DownmixerBufferProvider is only used for position masks.
174 if (audio_channel_mask_get_representation(channelMask)
175 == AUDIO_CHANNEL_REPRESENTATION_POSITION
176 && DownmixerBufferProvider::isMultichannelCapable()) {
Andy Hung66942552018-12-21 16:07:12 -0800177
178 // Check if we have a float or int16 downmixer, in that order.
179 for (const audio_format_t format : { AUDIO_FORMAT_PCM_FLOAT, AUDIO_FORMAT_PCM_16_BIT }) {
180 mDownmixerBufferProvider.reset(new DownmixerBufferProvider(
181 channelMask, mMixerChannelMask,
182 format,
183 sampleRate, sessionId, kCopyBufferFrameCount));
184 if (static_cast<DownmixerBufferProvider *>(mDownmixerBufferProvider.get())
185 ->isValid()) {
186 mDownmixRequiresFormat = format;
187 reconfigureBufferProviders();
188 return NO_ERROR;
189 }
Andy Hung34803d52014-07-16 21:41:35 -0700190 }
Andy Hung8ed196a2018-01-05 13:21:11 -0800191 // mDownmixerBufferProvider reset below.
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700192 }
Andy Hunge93b6b72014-07-17 21:30:53 -0700193
Andy Hungeda3e932021-10-21 13:44:56 -0700194 // See if we should use our built-in non-effect downmixer.
195 if (mMixerInFormat == AUDIO_FORMAT_PCM_FLOAT
Andy Hung8514b932023-04-14 17:58:02 -0700196 && ChannelMixBufferProvider::isOutputChannelMaskSupported(mMixerChannelMask)
Andy Hungeda3e932021-10-21 13:44:56 -0700197 && audio_channel_mask_get_representation(channelMask)
198 == AUDIO_CHANNEL_REPRESENTATION_POSITION) {
199 mDownmixerBufferProvider.reset(new ChannelMixBufferProvider(channelMask,
200 mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount));
201 if (static_cast<ChannelMixBufferProvider *>(mDownmixerBufferProvider.get())
202 ->isValid()) {
203 mDownmixRequiresFormat = mMixerInFormat;
204 reconfigureBufferProviders();
205 ALOGD("%s: Fallback using ChannelMix", __func__);
206 return NO_ERROR;
207 } else {
208 ALOGD("%s: ChannelMix not supported for channel mask %#x", __func__, channelMask);
209 }
210 }
211
Andy Hunge93b6b72014-07-17 21:30:53 -0700212 // Effect downmixer does not accept the channel conversion. Let's use our remixer.
Andy Hung8ed196a2018-01-05 13:21:11 -0800213 mDownmixerBufferProvider.reset(new RemixBufferProvider(channelMask,
214 mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount));
Andy Hunge93b6b72014-07-17 21:30:53 -0700215 // Remix always finds a conversion whereas Downmixer effect above may fail.
Andy Hung0f451e92014-08-04 21:28:47 -0700216 reconfigureBufferProviders();
Andy Hunge93b6b72014-07-17 21:30:53 -0700217 return NO_ERROR;
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700218}
219
Andy Hung8ed196a2018-01-05 13:21:11 -0800220void AudioMixer::Track::unprepareForReformat() {
Andy Hung0f451e92014-08-04 21:28:47 -0700221 ALOGV("AudioMixer::unprepareForReformat(%p)", this);
Andy Hung7f475492014-08-25 16:36:37 -0700222 bool requiresReconfigure = false;
Andy Hung8ed196a2018-01-05 13:21:11 -0800223 if (mReformatBufferProvider.get() != nullptr) {
224 mReformatBufferProvider.reset(nullptr);
Andy Hung7f475492014-08-25 16:36:37 -0700225 requiresReconfigure = true;
226 }
Andy Hung8ed196a2018-01-05 13:21:11 -0800227 if (mPostDownmixReformatBufferProvider.get() != nullptr) {
228 mPostDownmixReformatBufferProvider.reset(nullptr);
Andy Hung7f475492014-08-25 16:36:37 -0700229 requiresReconfigure = true;
230 }
231 if (requiresReconfigure) {
Andy Hung0f451e92014-08-04 21:28:47 -0700232 reconfigureBufferProviders();
Andy Hungef7c7fb2014-05-12 16:51:41 -0700233 }
234}
235
Andy Hung8ed196a2018-01-05 13:21:11 -0800236status_t AudioMixer::Track::prepareForReformat()
Andy Hungef7c7fb2014-05-12 16:51:41 -0700237{
Andy Hung0f451e92014-08-04 21:28:47 -0700238 ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat);
Andy Hung7f475492014-08-25 16:36:37 -0700239 // discard previous reformatters
Andy Hung0f451e92014-08-04 21:28:47 -0700240 unprepareForReformat();
Andy Hung7f475492014-08-25 16:36:37 -0700241 // only configure reformatters as needed
242 const audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID
243 ? mDownmixRequiresFormat : mMixerInFormat;
244 bool requiresReconfigure = false;
245 if (mFormat != targetFormat) {
Andy Hung8ed196a2018-01-05 13:21:11 -0800246 mReformatBufferProvider.reset(new ReformatBufferProvider(
Andy Hung0f451e92014-08-04 21:28:47 -0700247 audio_channel_count_from_out_mask(channelMask),
Andy Hung7f475492014-08-25 16:36:37 -0700248 mFormat,
249 targetFormat,
Andy Hung8ed196a2018-01-05 13:21:11 -0800250 kCopyBufferFrameCount));
Andy Hung7f475492014-08-25 16:36:37 -0700251 requiresReconfigure = true;
Kevin Rocarde053bfa2017-11-09 22:07:34 -0800252 } else if (mFormat == AUDIO_FORMAT_PCM_FLOAT) {
253 // Input and output are floats, make sure application did not provide > 3db samples
254 // that would break volume application (b/68099072)
255 // TODO: add a trusted source flag to avoid the overhead
256 mReformatBufferProvider.reset(new ClampFloatBufferProvider(
257 audio_channel_count_from_out_mask(channelMask),
258 kCopyBufferFrameCount));
259 requiresReconfigure = true;
Andy Hung7f475492014-08-25 16:36:37 -0700260 }
261 if (targetFormat != mMixerInFormat) {
Andy Hung8ed196a2018-01-05 13:21:11 -0800262 mPostDownmixReformatBufferProvider.reset(new ReformatBufferProvider(
Andy Hung7f475492014-08-25 16:36:37 -0700263 audio_channel_count_from_out_mask(mMixerChannelMask),
264 targetFormat,
265 mMixerInFormat,
Andy Hung8ed196a2018-01-05 13:21:11 -0800266 kCopyBufferFrameCount));
Andy Hung7f475492014-08-25 16:36:37 -0700267 requiresReconfigure = true;
268 }
269 if (requiresReconfigure) {
Andy Hung0f451e92014-08-04 21:28:47 -0700270 reconfigureBufferProviders();
Andy Hung296b7412014-06-17 15:25:47 -0700271 }
272 return NO_ERROR;
Andy Hungef7c7fb2014-05-12 16:51:41 -0700273}
274
jiabindce8f8c2018-12-10 17:49:31 -0800275void AudioMixer::Track::unprepareForAdjustChannels()
276{
277 ALOGV("AUDIOMIXER::unprepareForAdjustChannels");
278 if (mAdjustChannelsBufferProvider.get() != nullptr) {
279 mAdjustChannelsBufferProvider.reset(nullptr);
280 reconfigureBufferProviders();
281 }
282}
283
jiabin0e6babc2021-10-21 20:35:05 +0000284status_t AudioMixer::Track::prepareForAdjustChannels(size_t frames)
jiabindce8f8c2018-12-10 17:49:31 -0800285{
286 ALOGV("AudioMixer::prepareForAdjustChannels(%p) with inChannelCount: %u, outChannelCount: %u",
287 this, mAdjustInChannelCount, mAdjustOutChannelCount);
288 unprepareForAdjustChannels();
289 if (mAdjustInChannelCount != mAdjustOutChannelCount) {
jiabindce8f8c2018-12-10 17:49:31 -0800290 uint8_t* buffer = mKeepContractedChannels
291 ? (uint8_t*)mainBuffer + frames * audio_bytes_per_frame(
292 mMixerChannelCount, mMixerFormat)
jiabin0e6babc2021-10-21 20:35:05 +0000293 : nullptr;
294 mAdjustChannelsBufferProvider.reset(new AdjustChannelsBufferProvider(
295 mFormat, mAdjustInChannelCount, mAdjustOutChannelCount, frames,
296 mKeepContractedChannels ? mMixerFormat : AUDIO_FORMAT_INVALID,
297 buffer, mMixerHapticChannelCount));
jiabindce8f8c2018-12-10 17:49:31 -0800298 reconfigureBufferProviders();
299 }
300 return NO_ERROR;
301}
302
jiabinc658e452022-10-21 20:52:21 +0000303void AudioMixer::Track::unprepareForTee() {
304 ALOGV("AudioMixer::%s", __func__);
305 if (mTeeBufferProvider.get() != nullptr) {
306 mTeeBufferProvider.reset(nullptr);
307 reconfigureBufferProviders();
308 }
309}
310
311status_t AudioMixer::Track::prepareForTee() {
312 ALOGV("AudioMixer::%s(%p) teeBuffer=%p", __func__, this, teeBuffer);
313 unprepareForTee();
314 if (teeBuffer != nullptr) {
jiabinc658e452022-10-21 20:52:21 +0000315 mTeeBufferProvider.reset(new TeeBufferProvider(
jiabin76d94692022-12-15 21:51:21 +0000316 mInputFrameSize, mInputFrameSize, kCopyBufferFrameCount,
jiabinc658e452022-10-21 20:52:21 +0000317 (uint8_t*)teeBuffer, mTeeBufferFrameCount));
318 reconfigureBufferProviders();
319 }
320 return NO_ERROR;
321}
322
jiabindce8f8c2018-12-10 17:49:31 -0800323void AudioMixer::Track::clearContractedBuffer()
324{
jiabin0e6babc2021-10-21 20:35:05 +0000325 if (mAdjustChannelsBufferProvider.get() != nullptr) {
jiabinea8fa7a2019-02-22 14:41:50 -0800326 static_cast<AdjustChannelsBufferProvider*>(
jiabin0e6babc2021-10-21 20:35:05 +0000327 mAdjustChannelsBufferProvider.get())->clearContractedFrames();
jiabindce8f8c2018-12-10 17:49:31 -0800328 }
329}
330
jiabinc658e452022-10-21 20:52:21 +0000331void AudioMixer::Track::clearTeeFrameCopied() {
332 if (mTeeBufferProvider.get() != nullptr) {
333 static_cast<TeeBufferProvider*>(mTeeBufferProvider.get())->clearFramesCopied();
334 }
335}
336
Andy Hung8ed196a2018-01-05 13:21:11 -0800337void AudioMixer::Track::reconfigureBufferProviders()
Andy Hungef7c7fb2014-05-12 16:51:41 -0700338{
Andy Hung3a34df92018-08-21 12:32:30 -0700339 // configure from upstream to downstream buffer providers.
Andy Hung0f451e92014-08-04 21:28:47 -0700340 bufferProvider = mInputBufferProvider;
jiabinc658e452022-10-21 20:52:21 +0000341 if (mTeeBufferProvider != nullptr) {
342 mTeeBufferProvider->setBufferProvider(bufferProvider);
343 bufferProvider = mTeeBufferProvider.get();
344 }
jiabindce8f8c2018-12-10 17:49:31 -0800345 if (mAdjustChannelsBufferProvider.get() != nullptr) {
346 mAdjustChannelsBufferProvider->setBufferProvider(bufferProvider);
347 bufferProvider = mAdjustChannelsBufferProvider.get();
348 }
Andy Hung8ed196a2018-01-05 13:21:11 -0800349 if (mReformatBufferProvider.get() != nullptr) {
Andy Hung0f451e92014-08-04 21:28:47 -0700350 mReformatBufferProvider->setBufferProvider(bufferProvider);
Andy Hung8ed196a2018-01-05 13:21:11 -0800351 bufferProvider = mReformatBufferProvider.get();
Andy Hungef7c7fb2014-05-12 16:51:41 -0700352 }
Andy Hung8ed196a2018-01-05 13:21:11 -0800353 if (mDownmixerBufferProvider.get() != nullptr) {
354 mDownmixerBufferProvider->setBufferProvider(bufferProvider);
355 bufferProvider = mDownmixerBufferProvider.get();
Andy Hungef7c7fb2014-05-12 16:51:41 -0700356 }
Andy Hung8ed196a2018-01-05 13:21:11 -0800357 if (mPostDownmixReformatBufferProvider.get() != nullptr) {
Andy Hung7f475492014-08-25 16:36:37 -0700358 mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
Andy Hung8ed196a2018-01-05 13:21:11 -0800359 bufferProvider = mPostDownmixReformatBufferProvider.get();
Andy Hung7f475492014-08-25 16:36:37 -0700360 }
Andy Hung8ed196a2018-01-05 13:21:11 -0800361 if (mTimestretchBufferProvider.get() != nullptr) {
Andy Hungc5656cc2015-03-26 19:04:33 -0700362 mTimestretchBufferProvider->setBufferProvider(bufferProvider);
Andy Hung8ed196a2018-01-05 13:21:11 -0800363 bufferProvider = mTimestretchBufferProvider.get();
Andy Hungc5656cc2015-03-26 19:04:33 -0700364 }
Andy Hungef7c7fb2014-05-12 16:51:41 -0700365}
366
Glenn Kasten9c56d4a2011-12-19 15:06:39 -0800367void AudioMixer::setParameter(int name, int target, int param, void *value)
Mathias Agopian65ab4712010-07-14 17:59:35 -0700368{
Andy Hung1bc088a2018-02-09 15:57:31 -0800369 LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700370 const std::shared_ptr<Track> &track = getTrack(name);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700371
Kévin PETIT377b2ec2014-02-03 12:35:36 +0000372 int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
373 int32_t *valueBuf = reinterpret_cast<int32_t*>(value);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700374
375 switch (target) {
Glenn Kasten788040c2011-05-05 08:19:00 -0700376
Mathias Agopian65ab4712010-07-14 17:59:35 -0700377 case TRACK:
Glenn Kasten9c56d4a2011-12-19 15:06:39 -0800378 switch (param) {
Glenn Kasten788040c2011-05-05 08:19:00 -0700379 case CHANNEL_MASK: {
Andy Hunge93b6b72014-07-17 21:30:53 -0700380 const audio_channel_mask_t trackChannelMask =
381 static_cast<audio_channel_mask_t>(valueInt);
jiabin245cdd92018-12-07 17:55:15 -0800382 if (setChannelMasks(name, trackChannelMask,
Mikhail Naganov55773032020-10-01 15:08:13 -0700383 static_cast<audio_channel_mask_t>(
384 track->mMixerChannelMask | track->mMixerHapticChannelMask))) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700385 ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);
Andy Hung8ed196a2018-01-05 13:21:11 -0800386 invalidate();
Mathias Agopian65ab4712010-07-14 17:59:35 -0700387 }
Glenn Kasten788040c2011-05-05 08:19:00 -0700388 } break;
389 case MAIN_BUFFER:
Andy Hung8ed196a2018-01-05 13:21:11 -0800390 if (track->mainBuffer != valueBuf) {
391 track->mainBuffer = valueBuf;
Steve Block3856b092011-10-20 11:56:00 +0100392 ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
jiabindce8f8c2018-12-10 17:49:31 -0800393 if (track->mKeepContractedChannels) {
jiabin0e6babc2021-10-21 20:35:05 +0000394 track->prepareForAdjustChannels(mFrameCount);
jiabindce8f8c2018-12-10 17:49:31 -0800395 }
Andy Hung8ed196a2018-01-05 13:21:11 -0800396 invalidate();
Mathias Agopian65ab4712010-07-14 17:59:35 -0700397 }
Glenn Kasten788040c2011-05-05 08:19:00 -0700398 break;
399 case AUX_BUFFER:
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700400 AudioMixerBase::setParameter(name, target, param, value);
Glenn Kasten788040c2011-05-05 08:19:00 -0700401 break;
Andy Hungef7c7fb2014-05-12 16:51:41 -0700402 case FORMAT: {
403 audio_format_t format = static_cast<audio_format_t>(valueInt);
Andy Hung8ed196a2018-01-05 13:21:11 -0800404 if (track->mFormat != format) {
Andy Hungef7c7fb2014-05-12 16:51:41 -0700405 ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
Andy Hung8ed196a2018-01-05 13:21:11 -0800406 track->mFormat = format;
Andy Hungef7c7fb2014-05-12 16:51:41 -0700407 ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
Andy Hung8ed196a2018-01-05 13:21:11 -0800408 track->prepareForReformat();
409 invalidate();
Andy Hungef7c7fb2014-05-12 16:51:41 -0700410 }
411 } break;
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700412 // FIXME do we want to support setting the downmix type from AudioFlinger?
413 // for a specific track? or per mixer?
414 /* case DOWNMIX_TYPE:
415 break */
Andy Hung78820702014-02-28 16:23:02 -0800416 case MIXER_FORMAT: {
Andy Hunga1ab7cc2014-02-24 19:26:52 -0800417 audio_format_t format = static_cast<audio_format_t>(valueInt);
Andy Hung8ed196a2018-01-05 13:21:11 -0800418 if (track->mMixerFormat != format) {
419 track->mMixerFormat = format;
Andy Hung78820702014-02-28 16:23:02 -0800420 ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
jiabindce8f8c2018-12-10 17:49:31 -0800421 if (track->mKeepContractedChannels) {
jiabin0e6babc2021-10-21 20:35:05 +0000422 track->prepareForAdjustChannels(mFrameCount);
jiabindce8f8c2018-12-10 17:49:31 -0800423 }
Andy Hunga1ab7cc2014-02-24 19:26:52 -0800424 }
425 } break;
Andy Hunge93b6b72014-07-17 21:30:53 -0700426 case MIXER_CHANNEL_MASK: {
427 const audio_channel_mask_t mixerChannelMask =
428 static_cast<audio_channel_mask_t>(valueInt);
Mikhail Naganov55773032020-10-01 15:08:13 -0700429 if (setChannelMasks(name, static_cast<audio_channel_mask_t>(
430 track->channelMask | track->mHapticChannelMask),
jiabin245cdd92018-12-07 17:55:15 -0800431 mixerChannelMask)) {
Andy Hunge93b6b72014-07-17 21:30:53 -0700432 ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask);
Andy Hung8ed196a2018-01-05 13:21:11 -0800433 invalidate();
Andy Hunge93b6b72014-07-17 21:30:53 -0700434 }
435 } break;
jiabin245cdd92018-12-07 17:55:15 -0800436 case HAPTIC_ENABLED: {
437 const bool hapticPlaybackEnabled = static_cast<bool>(valueInt);
438 if (track->mHapticPlaybackEnabled != hapticPlaybackEnabled) {
439 track->mHapticPlaybackEnabled = hapticPlaybackEnabled;
440 track->mKeepContractedChannels = hapticPlaybackEnabled;
jiabin0e6babc2021-10-21 20:35:05 +0000441 track->prepareForAdjustChannels(mFrameCount);
jiabin245cdd92018-12-07 17:55:15 -0800442 }
443 } break;
Ahmad Khalil229466a2024-02-05 12:15:30 +0000444 case HAPTIC_SCALE: {
445 const os::HapticScale hapticScale = *reinterpret_cast<os::HapticScale*>(value);
446 if (track->mHapticScale != hapticScale) {
447 track->mHapticScale = hapticScale;
jiabin77270b82018-12-18 15:41:29 -0800448 }
449 } break;
Lais Andradebc3f37a2021-07-02 00:13:19 +0100450 case HAPTIC_MAX_AMPLITUDE: {
451 const float hapticMaxAmplitude = *reinterpret_cast<float*>(value);
452 if (track->mHapticMaxAmplitude != hapticMaxAmplitude) {
453 track->mHapticMaxAmplitude = hapticMaxAmplitude;
454 }
455 } break;
jiabinc658e452022-10-21 20:52:21 +0000456 case TEE_BUFFER:
457 if (track->teeBuffer != valueBuf) {
458 track->teeBuffer = valueBuf;
459 ALOGV("setParameter(TRACK, TEE_BUFFER, %p)", valueBuf);
460 track->prepareForTee();
461 }
462 break;
463 case TEE_BUFFER_FRAME_COUNT:
464 if (track->mTeeBufferFrameCount != valueInt) {
465 track->mTeeBufferFrameCount = valueInt;
466 ALOGV("setParameter(TRACK, TEE_BUFFER_FRAME_COUNT, %i)", valueInt);
467 track->prepareForTee();
468 }
469 break;
Glenn Kasten788040c2011-05-05 08:19:00 -0700470 default:
Glenn Kastenadad3d72014-02-21 14:51:43 -0800471 LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700472 }
Mathias Agopian65ab4712010-07-14 17:59:35 -0700473 break;
Glenn Kasten788040c2011-05-05 08:19:00 -0700474
Mathias Agopian65ab4712010-07-14 17:59:35 -0700475 case RESAMPLE:
Mathias Agopian65ab4712010-07-14 17:59:35 -0700476 case RAMP_VOLUME:
477 case VOLUME:
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700478 AudioMixerBase::setParameter(name, target, param, value);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700479 break;
Mikhail Naganov3b73e992019-07-31 14:53:29 -0700480 case TIMESTRETCH:
481 switch (param) {
482 case PLAYBACK_RATE: {
483 const AudioPlaybackRate *playbackRate =
484 reinterpret_cast<AudioPlaybackRate*>(value);
485 ALOGW_IF(!isAudioPlaybackRateValid(*playbackRate),
486 "bad parameters speed %f, pitch %f",
487 playbackRate->mSpeed, playbackRate->mPitch);
488 if (track->setPlaybackRate(*playbackRate)) {
489 ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
490 "%f %f %d %d",
491 playbackRate->mSpeed,
492 playbackRate->mPitch,
493 playbackRate->mStretchMode,
494 playbackRate->mFallbackMode);
495 // invalidate(); (should not require reconfigure)
Andy Hungc5656cc2015-03-26 19:04:33 -0700496 }
Mikhail Naganov3b73e992019-07-31 14:53:29 -0700497 } break;
498 default:
499 LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
500 }
501 break;
Glenn Kasten788040c2011-05-05 08:19:00 -0700502
503 default:
Glenn Kastenadad3d72014-02-21 14:51:43 -0800504 LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
Mathias Agopian65ab4712010-07-14 17:59:35 -0700505 }
Mathias Agopian65ab4712010-07-14 17:59:35 -0700506}
507
Andy Hung8ed196a2018-01-05 13:21:11 -0800508bool AudioMixer::Track::setPlaybackRate(const AudioPlaybackRate &playbackRate)
Andy Hungc5656cc2015-03-26 19:04:33 -0700509{
Andy Hung8ed196a2018-01-05 13:21:11 -0800510 if ((mTimestretchBufferProvider.get() == nullptr &&
Ricardo Garcia5a8a95d2015-04-18 14:47:04 -0700511 fabs(playbackRate.mSpeed - mPlaybackRate.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
512 fabs(playbackRate.mPitch - mPlaybackRate.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) ||
513 isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
Andy Hungc5656cc2015-03-26 19:04:33 -0700514 return false;
515 }
Ricardo Garcia5a8a95d2015-04-18 14:47:04 -0700516 mPlaybackRate = playbackRate;
Andy Hung8ed196a2018-01-05 13:21:11 -0800517 if (mTimestretchBufferProvider.get() == nullptr) {
Andy Hungc5656cc2015-03-26 19:04:33 -0700518 // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
519 // but if none exists, it is the channel count (1 for mono).
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700520 const int timestretchChannelCount = getOutputChannelCount();
Andy Hung8ed196a2018-01-05 13:21:11 -0800521 mTimestretchBufferProvider.reset(new TimestretchBufferProvider(timestretchChannelCount,
522 mMixerInFormat, sampleRate, playbackRate));
Andy Hungc5656cc2015-03-26 19:04:33 -0700523 reconfigureBufferProviders();
524 } else {
Andy Hung8ed196a2018-01-05 13:21:11 -0800525 static_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider.get())
Ricardo Garcia5a8a95d2015-04-18 14:47:04 -0700526 ->setPlaybackRate(playbackRate);
Andy Hungc5656cc2015-03-26 19:04:33 -0700527 }
528 return true;
529}
530
Glenn Kasten01c4ebf2012-02-22 10:47:35 -0800531void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)
Mathias Agopian65ab4712010-07-14 17:59:35 -0700532{
Andy Hung1bc088a2018-02-09 15:57:31 -0800533 LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700534 const std::shared_ptr<Track> &track = getTrack(name);
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700535
Andy Hung8ed196a2018-01-05 13:21:11 -0800536 if (track->mInputBufferProvider == bufferProvider) {
Andy Hung1d26ddf2014-05-29 15:53:09 -0700537 return; // don't reset any buffer providers if identical.
538 }
Andy Hung3a34df92018-08-21 12:32:30 -0700539 // reset order from downstream to upstream buffer providers.
540 if (track->mTimestretchBufferProvider.get() != nullptr) {
541 track->mTimestretchBufferProvider->reset();
Andy Hung8ed196a2018-01-05 13:21:11 -0800542 } else if (track->mPostDownmixReformatBufferProvider.get() != nullptr) {
543 track->mPostDownmixReformatBufferProvider->reset();
Andy Hung3a34df92018-08-21 12:32:30 -0700544 } else if (track->mDownmixerBufferProvider != nullptr) {
545 track->mDownmixerBufferProvider->reset();
546 } else if (track->mReformatBufferProvider.get() != nullptr) {
547 track->mReformatBufferProvider->reset();
jiabindce8f8c2018-12-10 17:49:31 -0800548 } else if (track->mAdjustChannelsBufferProvider.get() != nullptr) {
549 track->mAdjustChannelsBufferProvider->reset();
jiabinc658e452022-10-21 20:52:21 +0000550 } else if (track->mTeeBufferProvider.get() != nullptr) {
551 track->mTeeBufferProvider->reset();
Jean-Michel Trivi7d5b2622012-04-04 18:54:36 -0700552 }
Andy Hungef7c7fb2014-05-12 16:51:41 -0700553
Andy Hung8ed196a2018-01-05 13:21:11 -0800554 track->mInputBufferProvider = bufferProvider;
555 track->reconfigureBufferProviders();
Mathias Agopian65ab4712010-07-14 17:59:35 -0700556}
557
Glenn Kasten52008f82012-03-18 09:34:41 -0700558/*static*/ pthread_once_t AudioMixer::sOnceControl = PTHREAD_ONCE_INIT;
559
560/*static*/ void AudioMixer::sInitRoutine()
561{
Andy Hung34803d52014-07-16 21:41:35 -0700562 DownmixerBufferProvider::init(); // for the downmixer
John Grossman4ff14ba2012-02-08 16:37:41 -0800563}
564
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700565std::shared_ptr<AudioMixerBase::TrackBase> AudioMixer::preCreateTrack()
Andy Hunge93b6b72014-07-17 21:30:53 -0700566{
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700567 return std::make_shared<Track>();
Andy Hunge93b6b72014-07-17 21:30:53 -0700568}
569
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700570status_t AudioMixer::postCreateTrack(TrackBase *track)
Andy Hunge93b6b72014-07-17 21:30:53 -0700571{
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700572 Track* t = static_cast<Track*>(track);
573
574 audio_channel_mask_t channelMask = t->channelMask;
Mikhail Naganov55773032020-10-01 15:08:13 -0700575 t->mHapticChannelMask = static_cast<audio_channel_mask_t>(
576 channelMask & AUDIO_CHANNEL_HAPTIC_ALL);
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700577 t->mHapticChannelCount = audio_channel_count_from_out_mask(t->mHapticChannelMask);
Mikhail Naganov55773032020-10-01 15:08:13 -0700578 channelMask = static_cast<audio_channel_mask_t>(channelMask & ~AUDIO_CHANNEL_HAPTIC_ALL);
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700579 t->channelCount = audio_channel_count_from_out_mask(channelMask);
580 ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO,
581 "Non-stereo channel mask: %d\n", channelMask);
582 t->channelMask = channelMask;
583 t->mInputBufferProvider = NULL;
584 t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required
585 t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
586 // haptic
587 t->mHapticPlaybackEnabled = false;
Lais Andradee8995e92024-07-24 15:00:38 +0100588 t->mHapticScale = os::HapticScale::none();
Lais Andradebc3f37a2021-07-02 00:13:19 +0100589 t->mHapticMaxAmplitude = NAN;
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700590 t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
591 t->mMixerHapticChannelCount = 0;
592 t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
jiabin0e6babc2021-10-21 20:35:05 +0000593 t->mAdjustOutChannelCount = t->channelCount;
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700594 t->mKeepContractedChannels = false;
jiabin76d94692022-12-15 21:51:21 +0000595 t->mInputFrameSize = audio_bytes_per_frame(
596 t->channelCount + t->mHapticChannelCount, t->mFormat);
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700597 // Check the downmixing (or upmixing) requirements.
598 status_t status = t->prepareForDownmix();
599 if (status != OK) {
600 ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
601 return BAD_VALUE;
Andy Hunge93b6b72014-07-17 21:30:53 -0700602 }
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700603 // prepareForDownmix() may change mDownmixRequiresFormat
604 ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
605 t->prepareForReformat();
jiabin0e6babc2021-10-21 20:35:05 +0000606 t->prepareForAdjustChannels(mFrameCount);
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700607 return OK;
Andy Hunge93b6b72014-07-17 21:30:53 -0700608}
609
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700610void AudioMixer::preProcess()
Andy Hung5e58b0a2014-06-23 19:07:29 -0700611{
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700612 for (const auto &pair : mTracks) {
613 // Clear contracted buffer before processing if contracted channels are saved
614 const std::shared_ptr<TrackBase> &tb = pair.second;
615 Track *t = static_cast<Track*>(tb.get());
616 if (t->mKeepContractedChannels) {
617 t->clearContractedBuffer();
Andy Hung5e58b0a2014-06-23 19:07:29 -0700618 }
jiabinc658e452022-10-21 20:52:21 +0000619 t->clearTeeFrameCopied();
Andy Hung5e58b0a2014-06-23 19:07:29 -0700620 }
621}
622
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700623void AudioMixer::postProcess()
Andy Hung296b7412014-06-17 15:25:47 -0700624{
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700625 // Process haptic data.
jiabin77270b82018-12-18 15:41:29 -0800626 // Need to keep consistent with VibrationEffect.scale(int, float, int)
627 for (const auto &pair : mGroups) {
628 // process by group of tracks with same output main buffer.
629 const auto &group = pair.second;
630 for (const int name : group) {
Mikhail Naganov7ad7a252019-07-30 14:42:32 -0700631 const std::shared_ptr<Track> &t = getTrack(name);
jiabin77270b82018-12-18 15:41:29 -0800632 if (t->mHapticPlaybackEnabled) {
633 size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount;
jiabin77270b82018-12-18 15:41:29 -0800634 uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame(
635 t->mMixerChannelCount, t->mMixerFormat);
636 switch (t->mMixerFormat) {
637 // Mixer format should be AUDIO_FORMAT_PCM_FLOAT.
638 case AUDIO_FORMAT_PCM_FLOAT: {
Ahmad Khalil229466a2024-02-05 12:15:30 +0000639 os::scaleHapticData((float*) buffer, sampleCount, t->mHapticScale,
Lais Andradebc3f37a2021-07-02 00:13:19 +0100640 t->mHapticMaxAmplitude);
jiabin77270b82018-12-18 15:41:29 -0800641 } break;
642 default:
643 LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat);
644 break;
645 }
646 break;
647 }
jiabin76d94692022-12-15 21:51:21 +0000648 if (t->teeBuffer != nullptr && t->volumeRL == 0) {
649 // Need to mute tee
650 memset(t->teeBuffer, 0, t->mTeeBufferFrameCount * t->mInputFrameSize);
651 }
jiabin77270b82018-12-18 15:41:29 -0800652 }
653 }
654}
655
Mathias Agopian65ab4712010-07-14 17:59:35 -0700656// ----------------------------------------------------------------------------
Glenn Kasten63238ef2015-03-02 15:50:29 -0800657} // namespace android