| Glenn Kasten | 99e53b8 | 2012-01-19 08:59:58 -0800 | [diff] [blame] | 1 | /* | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 2 | ** | 
|  | 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 Kasten | 7f5d335 | 2013-02-15 23:55:04 +0000 | [diff] [blame] | 19 | //#define LOG_NDEBUG 0 | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 20 |  | 
| Mikhail Naganov | 3b73e99 | 2019-07-31 14:53:29 -0700 | [diff] [blame] | 21 | #include <sstream> | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 22 | #include <stdint.h> | 
|  | 23 | #include <string.h> | 
|  | 24 | #include <stdlib.h> | 
| Andy Hung | 5e58b0a | 2014-06-23 19:07:29 -0700 | [diff] [blame] | 25 | #include <math.h> | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 26 | #include <sys/types.h> | 
|  | 27 |  | 
|  | 28 | #include <utils/Errors.h> | 
|  | 29 | #include <utils/Log.h> | 
|  | 30 |  | 
| Jean-Michel Trivi | 0d255b2 | 2011-05-24 15:53:33 -0700 | [diff] [blame] | 31 | #include <system/audio.h> | 
|  | 32 |  | 
| Glenn Kasten | 3b21c50 | 2011-12-15 09:52:39 -0800 | [diff] [blame] | 33 | #include <audio_utils/primitives.h> | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 34 | #include <audio_utils/format.h> | 
| Andy Hung | 068561c | 2017-01-03 17:09:32 -0800 | [diff] [blame] | 35 | #include <media/AudioMixer.h> | 
| Jean-Michel Trivi | 7d5b262 | 2012-04-04 18:54:36 -0700 | [diff] [blame] | 36 |  | 
| Andy Hung | 296b741 | 2014-06-17 15:25:47 -0700 | [diff] [blame] | 37 | #include "AudioMixerOps.h" | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 38 |  | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 39 | // The FCC_2 macro refers to the Fixed Channel Count of 2 for the legacy integer mixer. | 
| Andy Hung | 296b741 | 2014-06-17 15:25:47 -0700 | [diff] [blame] | 40 | #ifndef FCC_2 | 
|  | 41 | #define FCC_2 2 | 
|  | 42 | #endif | 
|  | 43 |  | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 44 | // Look for MONO_HACK for any Mono hack involving legacy mono channel to | 
|  | 45 | // stereo channel conversion. | 
|  | 46 |  | 
| Andy Hung | 296b741 | 2014-06-17 15:25:47 -0700 | [diff] [blame] | 47 | /* 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 Hung | 1b2fdcb | 2014-07-16 17:44:34 -0700 | [diff] [blame] | 59 | // Set to default copy buffer size in frames for input processing. | 
| Mikhail Naganov | 3b73e99 | 2019-07-31 14:53:29 -0700 | [diff] [blame] | 60 | static constexpr size_t kCopyBufferFrameCount = 256; | 
| Andy Hung | 1b2fdcb | 2014-07-16 17:44:34 -0700 | [diff] [blame] | 61 |  | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 62 | namespace android { | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 63 |  | 
|  | 64 | // ---------------------------------------------------------------------------- | 
| Andy Hung | 1b2fdcb | 2014-07-16 17:44:34 -0700 | [diff] [blame] | 65 |  | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 66 | bool AudioMixer::isValidChannelMask(audio_channel_mask_t channelMask) const { | 
|  | 67 | return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible. | 
| Glenn Kasten | c5ac4cb | 2011-12-12 09:05:55 -0800 | [diff] [blame] | 68 | } | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 69 |  | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 70 | // Called when channel masks have changed for a track name | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 71 | // TODO: Fix DownmixerBufferProvider not to (possibly) change mixer input format, | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 72 | // which will simplify this logic. | 
|  | 73 | bool AudioMixer::setChannelMasks(int name, | 
|  | 74 | audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) { | 
| Andy Hung | 1bc088a | 2018-02-09 15:57:31 -0800 | [diff] [blame] | 75 | LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name); | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 76 | const std::shared_ptr<Track> &track = getTrack(name); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 77 |  | 
| jiabin | 245cdd9 | 2018-12-07 17:55:15 -0800 | [diff] [blame] | 78 | if (trackChannelMask == (track->channelMask | track->mHapticChannelMask) | 
|  | 79 | && mixerChannelMask == (track->mMixerChannelMask | track->mMixerHapticChannelMask)) { | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 80 | return false;  // no need to change | 
| Jean-Michel Trivi | 9bd2322 | 2012-04-16 13:43:48 -0700 | [diff] [blame] | 81 | } | 
| Mikhail Naganov | 5577303 | 2020-10-01 15:08:13 -0700 | [diff] [blame] | 82 | 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 Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 90 | // 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); | 
| jiabin | 245cdd9 | 2018-12-07 17:55:15 -0800 | [diff] [blame] | 93 | 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 Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 96 |  | 
|  | 97 | ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) | 
|  | 98 | && trackChannelCount | 
|  | 99 | && mixerChannelCount); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 100 | track->channelMask = trackChannelMask; | 
|  | 101 | track->channelCount = trackChannelCount; | 
|  | 102 | track->mMixerChannelMask = mixerChannelMask; | 
|  | 103 | track->mMixerChannelCount = mixerChannelCount; | 
| jiabin | 245cdd9 | 2018-12-07 17:55:15 -0800 | [diff] [blame] | 104 | 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; | 
|  | 111 | track->mAdjustOutChannelCount = track->channelCount + track->mMixerHapticChannelCount; | 
|  | 112 | track->mAdjustNonDestructiveInChannelCount = track->mAdjustOutChannelCount; | 
|  | 113 | track->mAdjustNonDestructiveOutChannelCount = track->channelCount; | 
|  | 114 | track->mKeepContractedChannels = track->mHapticPlaybackEnabled; | 
|  | 115 | } else { | 
|  | 116 | track->mAdjustInChannelCount = 0; | 
|  | 117 | track->mAdjustOutChannelCount = 0; | 
|  | 118 | track->mAdjustNonDestructiveInChannelCount = 0; | 
|  | 119 | track->mAdjustNonDestructiveOutChannelCount = 0; | 
|  | 120 | track->mKeepContractedChannels = false; | 
|  | 121 | } | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 122 |  | 
|  | 123 | // channel masks have changed, does this track need a downmixer? | 
|  | 124 | // update to try using our desired format (if we aren't already using it) | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 125 | const status_t status = track->prepareForDownmix(); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 126 | ALOGE_IF(status != OK, | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 127 | "prepareForDownmix error %d, track channel mask %#x, mixer channel mask %#x", | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 128 | status, track->channelMask, track->mMixerChannelMask); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 129 |  | 
| Yung Ti Su | 1a0ecc3 | 2018-05-07 11:09:15 +0800 | [diff] [blame] | 130 | // always do reformat since channel mask changed, | 
|  | 131 | // do it after downmix since track format may change! | 
|  | 132 | track->prepareForReformat(); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 133 |  | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 134 | track->prepareForAdjustChannelsNonDestructive(mFrameCount); | 
|  | 135 | track->prepareForAdjustChannels(); | 
|  | 136 |  | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 137 | // Resampler channels may have changed. | 
|  | 138 | track->recreateResampler(mSampleRate); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 139 | return true; | 
|  | 140 | } | 
|  | 141 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 142 | void AudioMixer::Track::unprepareForDownmix() { | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 143 | ALOGV("AudioMixer::unprepareForDownmix(%p)", this); | 
| Jean-Michel Trivi | 9bd2322 | 2012-04-16 13:43:48 -0700 | [diff] [blame] | 144 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 145 | if (mPostDownmixReformatBufferProvider.get() != nullptr) { | 
| Andy Hung | 8539589 | 2017-04-25 16:47:52 -0700 | [diff] [blame] | 146 | // release any buffers held by the mPostDownmixReformatBufferProvider | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 147 | // before deallocating the mDownmixerBufferProvider. | 
| Andy Hung | 8539589 | 2017-04-25 16:47:52 -0700 | [diff] [blame] | 148 | mPostDownmixReformatBufferProvider->reset(); | 
|  | 149 | } | 
|  | 150 |  | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 151 | mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 152 | if (mDownmixerBufferProvider.get() != nullptr) { | 
| Jean-Michel Trivi | 9bd2322 | 2012-04-16 13:43:48 -0700 | [diff] [blame] | 153 | // this track had previously been configured with a downmixer, delete it | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 154 | mDownmixerBufferProvider.reset(nullptr); | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 155 | reconfigureBufferProviders(); | 
| Jean-Michel Trivi | 9bd2322 | 2012-04-16 13:43:48 -0700 | [diff] [blame] | 156 | } else { | 
|  | 157 | ALOGV(" nothing to do, no downmixer to delete"); | 
|  | 158 | } | 
|  | 159 | } | 
|  | 160 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 161 | status_t AudioMixer::Track::prepareForDownmix() | 
| Jean-Michel Trivi | 7d5b262 | 2012-04-04 18:54:36 -0700 | [diff] [blame] | 162 | { | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 163 | ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x", | 
|  | 164 | this, channelMask); | 
| Jean-Michel Trivi | 7d5b262 | 2012-04-04 18:54:36 -0700 | [diff] [blame] | 165 |  | 
| Jean-Michel Trivi | 9bd2322 | 2012-04-16 13:43:48 -0700 | [diff] [blame] | 166 | // discard the previous downmixer if there was one | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 167 | unprepareForDownmix(); | 
| Andy Hung | 73e62e2 | 2015-04-20 12:06:38 -0700 | [diff] [blame] | 168 | // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks | 
| Judy Hsiao | c5cf9e2 | 2019-08-15 11:32:02 +0800 | [diff] [blame] | 169 | // are not the same and not handled internally, as mono for channel position masks is. | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 170 | if (channelMask == mMixerChannelMask | 
|  | 171 | || (channelMask == AUDIO_CHANNEL_OUT_MONO | 
| Judy Hsiao | c5cf9e2 | 2019-08-15 11:32:02 +0800 | [diff] [blame] | 172 | && isAudioChannelPositionMask(mMixerChannelMask))) { | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 173 | return NO_ERROR; | 
|  | 174 | } | 
| Andy Hung | 650ceb9 | 2015-01-29 13:31:12 -0800 | [diff] [blame] | 175 | // DownmixerBufferProvider is only used for position masks. | 
|  | 176 | if (audio_channel_mask_get_representation(channelMask) | 
|  | 177 | == AUDIO_CHANNEL_REPRESENTATION_POSITION | 
|  | 178 | && DownmixerBufferProvider::isMultichannelCapable()) { | 
| Andy Hung | 6694255 | 2018-12-21 16:07:12 -0800 | [diff] [blame] | 179 |  | 
|  | 180 | // Check if we have a float or int16 downmixer, in that order. | 
|  | 181 | for (const audio_format_t format : { AUDIO_FORMAT_PCM_FLOAT, AUDIO_FORMAT_PCM_16_BIT }) { | 
|  | 182 | mDownmixerBufferProvider.reset(new DownmixerBufferProvider( | 
|  | 183 | channelMask, mMixerChannelMask, | 
|  | 184 | format, | 
|  | 185 | sampleRate, sessionId, kCopyBufferFrameCount)); | 
|  | 186 | if (static_cast<DownmixerBufferProvider *>(mDownmixerBufferProvider.get()) | 
|  | 187 | ->isValid()) { | 
|  | 188 | mDownmixRequiresFormat = format; | 
|  | 189 | reconfigureBufferProviders(); | 
|  | 190 | return NO_ERROR; | 
|  | 191 | } | 
| Andy Hung | 34803d5 | 2014-07-16 21:41:35 -0700 | [diff] [blame] | 192 | } | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 193 | // mDownmixerBufferProvider reset below. | 
| Jean-Michel Trivi | 7d5b262 | 2012-04-04 18:54:36 -0700 | [diff] [blame] | 194 | } | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 195 |  | 
|  | 196 | // Effect downmixer does not accept the channel conversion.  Let's use our remixer. | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 197 | mDownmixerBufferProvider.reset(new RemixBufferProvider(channelMask, | 
|  | 198 | mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount)); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 199 | // Remix always finds a conversion whereas Downmixer effect above may fail. | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 200 | reconfigureBufferProviders(); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 201 | return NO_ERROR; | 
| Jean-Michel Trivi | 7d5b262 | 2012-04-04 18:54:36 -0700 | [diff] [blame] | 202 | } | 
|  | 203 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 204 | void AudioMixer::Track::unprepareForReformat() { | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 205 | ALOGV("AudioMixer::unprepareForReformat(%p)", this); | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 206 | bool requiresReconfigure = false; | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 207 | if (mReformatBufferProvider.get() != nullptr) { | 
|  | 208 | mReformatBufferProvider.reset(nullptr); | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 209 | requiresReconfigure = true; | 
|  | 210 | } | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 211 | if (mPostDownmixReformatBufferProvider.get() != nullptr) { | 
|  | 212 | mPostDownmixReformatBufferProvider.reset(nullptr); | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 213 | requiresReconfigure = true; | 
|  | 214 | } | 
|  | 215 | if (requiresReconfigure) { | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 216 | reconfigureBufferProviders(); | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 217 | } | 
|  | 218 | } | 
|  | 219 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 220 | status_t AudioMixer::Track::prepareForReformat() | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 221 | { | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 222 | ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat); | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 223 | // discard previous reformatters | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 224 | unprepareForReformat(); | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 225 | // only configure reformatters as needed | 
|  | 226 | const audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID | 
|  | 227 | ? mDownmixRequiresFormat : mMixerInFormat; | 
|  | 228 | bool requiresReconfigure = false; | 
|  | 229 | if (mFormat != targetFormat) { | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 230 | mReformatBufferProvider.reset(new ReformatBufferProvider( | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 231 | audio_channel_count_from_out_mask(channelMask), | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 232 | mFormat, | 
|  | 233 | targetFormat, | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 234 | kCopyBufferFrameCount)); | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 235 | requiresReconfigure = true; | 
| Kevin Rocard | e053bfa | 2017-11-09 22:07:34 -0800 | [diff] [blame] | 236 | } else if (mFormat == AUDIO_FORMAT_PCM_FLOAT) { | 
|  | 237 | // Input and output are floats, make sure application did not provide > 3db samples | 
|  | 238 | // that would break volume application (b/68099072) | 
|  | 239 | // TODO: add a trusted source flag to avoid the overhead | 
|  | 240 | mReformatBufferProvider.reset(new ClampFloatBufferProvider( | 
|  | 241 | audio_channel_count_from_out_mask(channelMask), | 
|  | 242 | kCopyBufferFrameCount)); | 
|  | 243 | requiresReconfigure = true; | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 244 | } | 
|  | 245 | if (targetFormat != mMixerInFormat) { | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 246 | mPostDownmixReformatBufferProvider.reset(new ReformatBufferProvider( | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 247 | audio_channel_count_from_out_mask(mMixerChannelMask), | 
|  | 248 | targetFormat, | 
|  | 249 | mMixerInFormat, | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 250 | kCopyBufferFrameCount)); | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 251 | requiresReconfigure = true; | 
|  | 252 | } | 
|  | 253 | if (requiresReconfigure) { | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 254 | reconfigureBufferProviders(); | 
| Andy Hung | 296b741 | 2014-06-17 15:25:47 -0700 | [diff] [blame] | 255 | } | 
|  | 256 | return NO_ERROR; | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 257 | } | 
|  | 258 |  | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 259 | void AudioMixer::Track::unprepareForAdjustChannels() | 
|  | 260 | { | 
|  | 261 | ALOGV("AUDIOMIXER::unprepareForAdjustChannels"); | 
|  | 262 | if (mAdjustChannelsBufferProvider.get() != nullptr) { | 
|  | 263 | mAdjustChannelsBufferProvider.reset(nullptr); | 
|  | 264 | reconfigureBufferProviders(); | 
|  | 265 | } | 
|  | 266 | } | 
|  | 267 |  | 
|  | 268 | status_t AudioMixer::Track::prepareForAdjustChannels() | 
|  | 269 | { | 
|  | 270 | ALOGV("AudioMixer::prepareForAdjustChannels(%p) with inChannelCount: %u, outChannelCount: %u", | 
|  | 271 | this, mAdjustInChannelCount, mAdjustOutChannelCount); | 
|  | 272 | unprepareForAdjustChannels(); | 
|  | 273 | if (mAdjustInChannelCount != mAdjustOutChannelCount) { | 
|  | 274 | mAdjustChannelsBufferProvider.reset(new AdjustChannelsBufferProvider( | 
|  | 275 | mFormat, mAdjustInChannelCount, mAdjustOutChannelCount, kCopyBufferFrameCount)); | 
|  | 276 | reconfigureBufferProviders(); | 
|  | 277 | } | 
|  | 278 | return NO_ERROR; | 
|  | 279 | } | 
|  | 280 |  | 
|  | 281 | void AudioMixer::Track::unprepareForAdjustChannelsNonDestructive() | 
|  | 282 | { | 
|  | 283 | ALOGV("AUDIOMIXER::unprepareForAdjustChannelsNonDestructive"); | 
| jiabin | ea8fa7a | 2019-02-22 14:41:50 -0800 | [diff] [blame] | 284 | if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) { | 
|  | 285 | mContractChannelsNonDestructiveBufferProvider.reset(nullptr); | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 286 | reconfigureBufferProviders(); | 
|  | 287 | } | 
|  | 288 | } | 
|  | 289 |  | 
|  | 290 | status_t AudioMixer::Track::prepareForAdjustChannelsNonDestructive(size_t frames) | 
|  | 291 | { | 
|  | 292 | ALOGV("AudioMixer::prepareForAdjustChannelsNonDestructive(%p) with inChannelCount: %u, " | 
|  | 293 | "outChannelCount: %u, keepContractedChannels: %d", | 
|  | 294 | this, mAdjustNonDestructiveInChannelCount, mAdjustNonDestructiveOutChannelCount, | 
|  | 295 | mKeepContractedChannels); | 
|  | 296 | unprepareForAdjustChannelsNonDestructive(); | 
|  | 297 | if (mAdjustNonDestructiveInChannelCount != mAdjustNonDestructiveOutChannelCount) { | 
|  | 298 | uint8_t* buffer = mKeepContractedChannels | 
|  | 299 | ? (uint8_t*)mainBuffer + frames * audio_bytes_per_frame( | 
|  | 300 | mMixerChannelCount, mMixerFormat) | 
|  | 301 | : NULL; | 
| jiabin | ea8fa7a | 2019-02-22 14:41:50 -0800 | [diff] [blame] | 302 | mContractChannelsNonDestructiveBufferProvider.reset( | 
|  | 303 | new AdjustChannelsBufferProvider( | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 304 | mFormat, | 
|  | 305 | mAdjustNonDestructiveInChannelCount, | 
|  | 306 | mAdjustNonDestructiveOutChannelCount, | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 307 | frames, | 
| jiabin | ea8fa7a | 2019-02-22 14:41:50 -0800 | [diff] [blame] | 308 | mKeepContractedChannels ? mMixerFormat : AUDIO_FORMAT_INVALID, | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 309 | buffer)); | 
|  | 310 | reconfigureBufferProviders(); | 
|  | 311 | } | 
|  | 312 | return NO_ERROR; | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | void AudioMixer::Track::clearContractedBuffer() | 
|  | 316 | { | 
| jiabin | ea8fa7a | 2019-02-22 14:41:50 -0800 | [diff] [blame] | 317 | if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) { | 
|  | 318 | static_cast<AdjustChannelsBufferProvider*>( | 
|  | 319 | mContractChannelsNonDestructiveBufferProvider.get())->clearContractedFrames(); | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 320 | } | 
|  | 321 | } | 
|  | 322 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 323 | void AudioMixer::Track::reconfigureBufferProviders() | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 324 | { | 
| Andy Hung | 3a34df9 | 2018-08-21 12:32:30 -0700 | [diff] [blame] | 325 | // configure from upstream to downstream buffer providers. | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 326 | bufferProvider = mInputBufferProvider; | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 327 | if (mAdjustChannelsBufferProvider.get() != nullptr) { | 
|  | 328 | mAdjustChannelsBufferProvider->setBufferProvider(bufferProvider); | 
|  | 329 | bufferProvider = mAdjustChannelsBufferProvider.get(); | 
|  | 330 | } | 
| jiabin | ea8fa7a | 2019-02-22 14:41:50 -0800 | [diff] [blame] | 331 | if (mContractChannelsNonDestructiveBufferProvider.get() != nullptr) { | 
|  | 332 | mContractChannelsNonDestructiveBufferProvider->setBufferProvider(bufferProvider); | 
|  | 333 | bufferProvider = mContractChannelsNonDestructiveBufferProvider.get(); | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 334 | } | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 335 | if (mReformatBufferProvider.get() != nullptr) { | 
| Andy Hung | 0f451e9 | 2014-08-04 21:28:47 -0700 | [diff] [blame] | 336 | mReformatBufferProvider->setBufferProvider(bufferProvider); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 337 | bufferProvider = mReformatBufferProvider.get(); | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 338 | } | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 339 | if (mDownmixerBufferProvider.get() != nullptr) { | 
|  | 340 | mDownmixerBufferProvider->setBufferProvider(bufferProvider); | 
|  | 341 | bufferProvider = mDownmixerBufferProvider.get(); | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 342 | } | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 343 | if (mPostDownmixReformatBufferProvider.get() != nullptr) { | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 344 | mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 345 | bufferProvider = mPostDownmixReformatBufferProvider.get(); | 
| Andy Hung | 7f47549 | 2014-08-25 16:36:37 -0700 | [diff] [blame] | 346 | } | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 347 | if (mTimestretchBufferProvider.get() != nullptr) { | 
| Andy Hung | c5656cc | 2015-03-26 19:04:33 -0700 | [diff] [blame] | 348 | mTimestretchBufferProvider->setBufferProvider(bufferProvider); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 349 | bufferProvider = mTimestretchBufferProvider.get(); | 
| Andy Hung | c5656cc | 2015-03-26 19:04:33 -0700 | [diff] [blame] | 350 | } | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 351 | } | 
|  | 352 |  | 
| Glenn Kasten | 9c56d4a | 2011-12-19 15:06:39 -0800 | [diff] [blame] | 353 | void AudioMixer::setParameter(int name, int target, int param, void *value) | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 354 | { | 
| Andy Hung | 1bc088a | 2018-02-09 15:57:31 -0800 | [diff] [blame] | 355 | LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name); | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 356 | const std::shared_ptr<Track> &track = getTrack(name); | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 357 |  | 
| Kévin PETIT | 377b2ec | 2014-02-03 12:35:36 +0000 | [diff] [blame] | 358 | int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value)); | 
|  | 359 | int32_t *valueBuf = reinterpret_cast<int32_t*>(value); | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 360 |  | 
|  | 361 | switch (target) { | 
| Glenn Kasten | 788040c | 2011-05-05 08:19:00 -0700 | [diff] [blame] | 362 |  | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 363 | case TRACK: | 
| Glenn Kasten | 9c56d4a | 2011-12-19 15:06:39 -0800 | [diff] [blame] | 364 | switch (param) { | 
| Glenn Kasten | 788040c | 2011-05-05 08:19:00 -0700 | [diff] [blame] | 365 | case CHANNEL_MASK: { | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 366 | const audio_channel_mask_t trackChannelMask = | 
|  | 367 | static_cast<audio_channel_mask_t>(valueInt); | 
| jiabin | 245cdd9 | 2018-12-07 17:55:15 -0800 | [diff] [blame] | 368 | if (setChannelMasks(name, trackChannelMask, | 
| Mikhail Naganov | 5577303 | 2020-10-01 15:08:13 -0700 | [diff] [blame] | 369 | static_cast<audio_channel_mask_t>( | 
|  | 370 | track->mMixerChannelMask | track->mMixerHapticChannelMask))) { | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 371 | ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 372 | invalidate(); | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 373 | } | 
| Glenn Kasten | 788040c | 2011-05-05 08:19:00 -0700 | [diff] [blame] | 374 | } break; | 
|  | 375 | case MAIN_BUFFER: | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 376 | if (track->mainBuffer != valueBuf) { | 
|  | 377 | track->mainBuffer = valueBuf; | 
| Steve Block | 3856b09 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 378 | ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf); | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 379 | if (track->mKeepContractedChannels) { | 
|  | 380 | track->prepareForAdjustChannelsNonDestructive(mFrameCount); | 
|  | 381 | } | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 382 | invalidate(); | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 383 | } | 
| Glenn Kasten | 788040c | 2011-05-05 08:19:00 -0700 | [diff] [blame] | 384 | break; | 
|  | 385 | case AUX_BUFFER: | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 386 | AudioMixerBase::setParameter(name, target, param, value); | 
| Glenn Kasten | 788040c | 2011-05-05 08:19:00 -0700 | [diff] [blame] | 387 | break; | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 388 | case FORMAT: { | 
|  | 389 | audio_format_t format = static_cast<audio_format_t>(valueInt); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 390 | if (track->mFormat != format) { | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 391 | ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 392 | track->mFormat = format; | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 393 | ALOGV("setParameter(TRACK, FORMAT, %#x)", format); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 394 | track->prepareForReformat(); | 
|  | 395 | invalidate(); | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 396 | } | 
|  | 397 | } break; | 
| Jean-Michel Trivi | 7d5b262 | 2012-04-04 18:54:36 -0700 | [diff] [blame] | 398 | // FIXME do we want to support setting the downmix type from AudioFlinger? | 
|  | 399 | //         for a specific track? or per mixer? | 
|  | 400 | /* case DOWNMIX_TYPE: | 
|  | 401 | break          */ | 
| Andy Hung | 7882070 | 2014-02-28 16:23:02 -0800 | [diff] [blame] | 402 | case MIXER_FORMAT: { | 
| Andy Hung | a1ab7cc | 2014-02-24 19:26:52 -0800 | [diff] [blame] | 403 | audio_format_t format = static_cast<audio_format_t>(valueInt); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 404 | if (track->mMixerFormat != format) { | 
|  | 405 | track->mMixerFormat = format; | 
| Andy Hung | 7882070 | 2014-02-28 16:23:02 -0800 | [diff] [blame] | 406 | ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format); | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 407 | if (track->mKeepContractedChannels) { | 
|  | 408 | track->prepareForAdjustChannelsNonDestructive(mFrameCount); | 
|  | 409 | } | 
| Andy Hung | a1ab7cc | 2014-02-24 19:26:52 -0800 | [diff] [blame] | 410 | } | 
|  | 411 | } break; | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 412 | case MIXER_CHANNEL_MASK: { | 
|  | 413 | const audio_channel_mask_t mixerChannelMask = | 
|  | 414 | static_cast<audio_channel_mask_t>(valueInt); | 
| Mikhail Naganov | 5577303 | 2020-10-01 15:08:13 -0700 | [diff] [blame] | 415 | if (setChannelMasks(name, static_cast<audio_channel_mask_t>( | 
|  | 416 | track->channelMask | track->mHapticChannelMask), | 
| jiabin | 245cdd9 | 2018-12-07 17:55:15 -0800 | [diff] [blame] | 417 | mixerChannelMask)) { | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 418 | ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 419 | invalidate(); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 420 | } | 
|  | 421 | } break; | 
| jiabin | 245cdd9 | 2018-12-07 17:55:15 -0800 | [diff] [blame] | 422 | case HAPTIC_ENABLED: { | 
|  | 423 | const bool hapticPlaybackEnabled = static_cast<bool>(valueInt); | 
|  | 424 | if (track->mHapticPlaybackEnabled != hapticPlaybackEnabled) { | 
|  | 425 | track->mHapticPlaybackEnabled = hapticPlaybackEnabled; | 
|  | 426 | track->mKeepContractedChannels = hapticPlaybackEnabled; | 
|  | 427 | track->prepareForAdjustChannelsNonDestructive(mFrameCount); | 
|  | 428 | track->prepareForAdjustChannels(); | 
|  | 429 | } | 
|  | 430 | } break; | 
| jiabin | 77270b8 | 2018-12-18 15:41:29 -0800 | [diff] [blame] | 431 | case HAPTIC_INTENSITY: { | 
| jiabin | e70bc7f | 2020-06-30 22:07:55 -0700 | [diff] [blame] | 432 | const os::HapticScale hapticIntensity = static_cast<os::HapticScale>(valueInt); | 
| jiabin | 77270b8 | 2018-12-18 15:41:29 -0800 | [diff] [blame] | 433 | if (track->mHapticIntensity != hapticIntensity) { | 
|  | 434 | track->mHapticIntensity = hapticIntensity; | 
|  | 435 | } | 
|  | 436 | } break; | 
| Glenn Kasten | 788040c | 2011-05-05 08:19:00 -0700 | [diff] [blame] | 437 | default: | 
| Glenn Kasten | adad3d7 | 2014-02-21 14:51:43 -0800 | [diff] [blame] | 438 | LOG_ALWAYS_FATAL("setParameter track: bad param %d", param); | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 439 | } | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 440 | break; | 
| Glenn Kasten | 788040c | 2011-05-05 08:19:00 -0700 | [diff] [blame] | 441 |  | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 442 | case RESAMPLE: | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 443 | case RAMP_VOLUME: | 
|  | 444 | case VOLUME: | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 445 | AudioMixerBase::setParameter(name, target, param, value); | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 446 | break; | 
| Mikhail Naganov | 3b73e99 | 2019-07-31 14:53:29 -0700 | [diff] [blame] | 447 | case TIMESTRETCH: | 
|  | 448 | switch (param) { | 
|  | 449 | case PLAYBACK_RATE: { | 
|  | 450 | const AudioPlaybackRate *playbackRate = | 
|  | 451 | reinterpret_cast<AudioPlaybackRate*>(value); | 
|  | 452 | ALOGW_IF(!isAudioPlaybackRateValid(*playbackRate), | 
|  | 453 | "bad parameters speed %f, pitch %f", | 
|  | 454 | playbackRate->mSpeed, playbackRate->mPitch); | 
|  | 455 | if (track->setPlaybackRate(*playbackRate)) { | 
|  | 456 | ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE " | 
|  | 457 | "%f %f %d %d", | 
|  | 458 | playbackRate->mSpeed, | 
|  | 459 | playbackRate->mPitch, | 
|  | 460 | playbackRate->mStretchMode, | 
|  | 461 | playbackRate->mFallbackMode); | 
|  | 462 | // invalidate();  (should not require reconfigure) | 
| Andy Hung | c5656cc | 2015-03-26 19:04:33 -0700 | [diff] [blame] | 463 | } | 
| Mikhail Naganov | 3b73e99 | 2019-07-31 14:53:29 -0700 | [diff] [blame] | 464 | } break; | 
|  | 465 | default: | 
|  | 466 | LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param); | 
|  | 467 | } | 
|  | 468 | break; | 
| Glenn Kasten | 788040c | 2011-05-05 08:19:00 -0700 | [diff] [blame] | 469 |  | 
|  | 470 | default: | 
| Glenn Kasten | adad3d7 | 2014-02-21 14:51:43 -0800 | [diff] [blame] | 471 | LOG_ALWAYS_FATAL("setParameter: bad target %d", target); | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 472 | } | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 473 | } | 
|  | 474 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 475 | bool AudioMixer::Track::setPlaybackRate(const AudioPlaybackRate &playbackRate) | 
| Andy Hung | c5656cc | 2015-03-26 19:04:33 -0700 | [diff] [blame] | 476 | { | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 477 | if ((mTimestretchBufferProvider.get() == nullptr && | 
| Ricardo Garcia | 5a8a95d | 2015-04-18 14:47:04 -0700 | [diff] [blame] | 478 | fabs(playbackRate.mSpeed - mPlaybackRate.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA && | 
|  | 479 | fabs(playbackRate.mPitch - mPlaybackRate.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) || | 
|  | 480 | isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) { | 
| Andy Hung | c5656cc | 2015-03-26 19:04:33 -0700 | [diff] [blame] | 481 | return false; | 
|  | 482 | } | 
| Ricardo Garcia | 5a8a95d | 2015-04-18 14:47:04 -0700 | [diff] [blame] | 483 | mPlaybackRate = playbackRate; | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 484 | if (mTimestretchBufferProvider.get() == nullptr) { | 
| Andy Hung | c5656cc | 2015-03-26 19:04:33 -0700 | [diff] [blame] | 485 | // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer | 
|  | 486 | // but if none exists, it is the channel count (1 for mono). | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 487 | const int timestretchChannelCount = getOutputChannelCount(); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 488 | mTimestretchBufferProvider.reset(new TimestretchBufferProvider(timestretchChannelCount, | 
|  | 489 | mMixerInFormat, sampleRate, playbackRate)); | 
| Andy Hung | c5656cc | 2015-03-26 19:04:33 -0700 | [diff] [blame] | 490 | reconfigureBufferProviders(); | 
|  | 491 | } else { | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 492 | static_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider.get()) | 
| Ricardo Garcia | 5a8a95d | 2015-04-18 14:47:04 -0700 | [diff] [blame] | 493 | ->setPlaybackRate(playbackRate); | 
| Andy Hung | c5656cc | 2015-03-26 19:04:33 -0700 | [diff] [blame] | 494 | } | 
|  | 495 | return true; | 
|  | 496 | } | 
|  | 497 |  | 
| Glenn Kasten | 01c4ebf | 2012-02-22 10:47:35 -0800 | [diff] [blame] | 498 | void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider) | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 499 | { | 
| Andy Hung | 1bc088a | 2018-02-09 15:57:31 -0800 | [diff] [blame] | 500 | LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name); | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 501 | const std::shared_ptr<Track> &track = getTrack(name); | 
| Jean-Michel Trivi | 7d5b262 | 2012-04-04 18:54:36 -0700 | [diff] [blame] | 502 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 503 | if (track->mInputBufferProvider == bufferProvider) { | 
| Andy Hung | 1d26ddf | 2014-05-29 15:53:09 -0700 | [diff] [blame] | 504 | return; // don't reset any buffer providers if identical. | 
|  | 505 | } | 
| Andy Hung | 3a34df9 | 2018-08-21 12:32:30 -0700 | [diff] [blame] | 506 | // reset order from downstream to upstream buffer providers. | 
|  | 507 | if (track->mTimestretchBufferProvider.get() != nullptr) { | 
|  | 508 | track->mTimestretchBufferProvider->reset(); | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 509 | } else if (track->mPostDownmixReformatBufferProvider.get() != nullptr) { | 
|  | 510 | track->mPostDownmixReformatBufferProvider->reset(); | 
| Andy Hung | 3a34df9 | 2018-08-21 12:32:30 -0700 | [diff] [blame] | 511 | } else if (track->mDownmixerBufferProvider != nullptr) { | 
|  | 512 | track->mDownmixerBufferProvider->reset(); | 
|  | 513 | } else if (track->mReformatBufferProvider.get() != nullptr) { | 
|  | 514 | track->mReformatBufferProvider->reset(); | 
| jiabin | ea8fa7a | 2019-02-22 14:41:50 -0800 | [diff] [blame] | 515 | } else if (track->mContractChannelsNonDestructiveBufferProvider.get() != nullptr) { | 
|  | 516 | track->mContractChannelsNonDestructiveBufferProvider->reset(); | 
| jiabin | dce8f8c | 2018-12-10 17:49:31 -0800 | [diff] [blame] | 517 | } else if (track->mAdjustChannelsBufferProvider.get() != nullptr) { | 
|  | 518 | track->mAdjustChannelsBufferProvider->reset(); | 
| Jean-Michel Trivi | 7d5b262 | 2012-04-04 18:54:36 -0700 | [diff] [blame] | 519 | } | 
| Andy Hung | ef7c7fb | 2014-05-12 16:51:41 -0700 | [diff] [blame] | 520 |  | 
| Andy Hung | 8ed196a | 2018-01-05 13:21:11 -0800 | [diff] [blame] | 521 | track->mInputBufferProvider = bufferProvider; | 
|  | 522 | track->reconfigureBufferProviders(); | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 523 | } | 
|  | 524 |  | 
| Glenn Kasten | 52008f8 | 2012-03-18 09:34:41 -0700 | [diff] [blame] | 525 | /*static*/ pthread_once_t AudioMixer::sOnceControl = PTHREAD_ONCE_INIT; | 
|  | 526 |  | 
|  | 527 | /*static*/ void AudioMixer::sInitRoutine() | 
|  | 528 | { | 
| Andy Hung | 34803d5 | 2014-07-16 21:41:35 -0700 | [diff] [blame] | 529 | DownmixerBufferProvider::init(); // for the downmixer | 
| John Grossman | 4ff14ba | 2012-02-08 16:37:41 -0800 | [diff] [blame] | 530 | } | 
|  | 531 |  | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 532 | std::shared_ptr<AudioMixerBase::TrackBase> AudioMixer::preCreateTrack() | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 533 | { | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 534 | return std::make_shared<Track>(); | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 535 | } | 
|  | 536 |  | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 537 | status_t AudioMixer::postCreateTrack(TrackBase *track) | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 538 | { | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 539 | Track* t = static_cast<Track*>(track); | 
|  | 540 |  | 
|  | 541 | audio_channel_mask_t channelMask = t->channelMask; | 
| Mikhail Naganov | 5577303 | 2020-10-01 15:08:13 -0700 | [diff] [blame] | 542 | t->mHapticChannelMask = static_cast<audio_channel_mask_t>( | 
|  | 543 | channelMask & AUDIO_CHANNEL_HAPTIC_ALL); | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 544 | t->mHapticChannelCount = audio_channel_count_from_out_mask(t->mHapticChannelMask); | 
| Mikhail Naganov | 5577303 | 2020-10-01 15:08:13 -0700 | [diff] [blame] | 545 | channelMask = static_cast<audio_channel_mask_t>(channelMask & ~AUDIO_CHANNEL_HAPTIC_ALL); | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 546 | t->channelCount = audio_channel_count_from_out_mask(channelMask); | 
|  | 547 | ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO, | 
|  | 548 | "Non-stereo channel mask: %d\n", channelMask); | 
|  | 549 | t->channelMask = channelMask; | 
|  | 550 | t->mInputBufferProvider = NULL; | 
|  | 551 | t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required | 
|  | 552 | t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; | 
|  | 553 | // haptic | 
|  | 554 | t->mHapticPlaybackEnabled = false; | 
| jiabin | e70bc7f | 2020-06-30 22:07:55 -0700 | [diff] [blame] | 555 | t->mHapticIntensity = os::HapticScale::NONE; | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 556 | t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE; | 
|  | 557 | t->mMixerHapticChannelCount = 0; | 
|  | 558 | t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount; | 
|  | 559 | t->mAdjustOutChannelCount = t->channelCount + t->mMixerHapticChannelCount; | 
|  | 560 | t->mAdjustNonDestructiveInChannelCount = t->mAdjustOutChannelCount; | 
|  | 561 | t->mAdjustNonDestructiveOutChannelCount = t->channelCount; | 
|  | 562 | t->mKeepContractedChannels = false; | 
|  | 563 | // Check the downmixing (or upmixing) requirements. | 
|  | 564 | status_t status = t->prepareForDownmix(); | 
|  | 565 | if (status != OK) { | 
|  | 566 | ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask); | 
|  | 567 | return BAD_VALUE; | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 568 | } | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 569 | // prepareForDownmix() may change mDownmixRequiresFormat | 
|  | 570 | ALOGVV("mMixerFormat:%#x  mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat); | 
|  | 571 | t->prepareForReformat(); | 
|  | 572 | t->prepareForAdjustChannelsNonDestructive(mFrameCount); | 
|  | 573 | t->prepareForAdjustChannels(); | 
|  | 574 | return OK; | 
| Andy Hung | e93b6b7 | 2014-07-17 21:30:53 -0700 | [diff] [blame] | 575 | } | 
|  | 576 |  | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 577 | void AudioMixer::preProcess() | 
| Andy Hung | 5e58b0a | 2014-06-23 19:07:29 -0700 | [diff] [blame] | 578 | { | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 579 | for (const auto &pair : mTracks) { | 
|  | 580 | // Clear contracted buffer before processing if contracted channels are saved | 
|  | 581 | const std::shared_ptr<TrackBase> &tb = pair.second; | 
|  | 582 | Track *t = static_cast<Track*>(tb.get()); | 
|  | 583 | if (t->mKeepContractedChannels) { | 
|  | 584 | t->clearContractedBuffer(); | 
| Andy Hung | 5e58b0a | 2014-06-23 19:07:29 -0700 | [diff] [blame] | 585 | } | 
|  | 586 | } | 
|  | 587 | } | 
|  | 588 |  | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 589 | void AudioMixer::postProcess() | 
| Andy Hung | 296b741 | 2014-06-17 15:25:47 -0700 | [diff] [blame] | 590 | { | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 591 | // Process haptic data. | 
| jiabin | 77270b8 | 2018-12-18 15:41:29 -0800 | [diff] [blame] | 592 | // Need to keep consistent with VibrationEffect.scale(int, float, int) | 
|  | 593 | for (const auto &pair : mGroups) { | 
|  | 594 | // process by group of tracks with same output main buffer. | 
|  | 595 | const auto &group = pair.second; | 
|  | 596 | for (const int name : group) { | 
| Mikhail Naganov | 7ad7a25 | 2019-07-30 14:42:32 -0700 | [diff] [blame] | 597 | const std::shared_ptr<Track> &t = getTrack(name); | 
| jiabin | 77270b8 | 2018-12-18 15:41:29 -0800 | [diff] [blame] | 598 | if (t->mHapticPlaybackEnabled) { | 
|  | 599 | size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount; | 
| jiabin | 77270b8 | 2018-12-18 15:41:29 -0800 | [diff] [blame] | 600 | uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame( | 
|  | 601 | t->mMixerChannelCount, t->mMixerFormat); | 
|  | 602 | switch (t->mMixerFormat) { | 
|  | 603 | // Mixer format should be AUDIO_FORMAT_PCM_FLOAT. | 
|  | 604 | case AUDIO_FORMAT_PCM_FLOAT: { | 
| jiabin | e70bc7f | 2020-06-30 22:07:55 -0700 | [diff] [blame] | 605 | os::scaleHapticData((float*) buffer, sampleCount, t->mHapticIntensity); | 
| jiabin | 77270b8 | 2018-12-18 15:41:29 -0800 | [diff] [blame] | 606 | } break; | 
|  | 607 | default: | 
|  | 608 | LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat); | 
|  | 609 | break; | 
|  | 610 | } | 
|  | 611 | break; | 
|  | 612 | } | 
|  | 613 | } | 
|  | 614 | } | 
|  | 615 | } | 
|  | 616 |  | 
| Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 617 | // ---------------------------------------------------------------------------- | 
| Glenn Kasten | 63238ef | 2015-03-02 15:50:29 -0800 | [diff] [blame] | 618 | } // namespace android |