1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 23 import com.android.internal.util.Preconditions; 24 25 import java.util.ArrayList; 26 27 /** 28 * The HwAudioSource represents the audio playback directly from a source audio device. 29 * It currently supports {@link HwAudioSource#start()} and {@link HwAudioSource#stop()} only 30 * corresponding to {@link AudioSystem#startAudioSource(AudioPortConfig, AudioAttributes)} 31 * and {@link AudioSystem#stopAudioSource(int)}. 32 * 33 * @hide 34 */ 35 @SystemApi 36 public class HwAudioSource extends PlayerBase { 37 private final AudioDeviceInfo mAudioDeviceInfo; 38 private final AudioAttributes mAudioAttributes; 39 40 /** 41 * The value of the native handle encodes the HwAudioSource state. 42 * The native handle returned by {@link AudioSystem#startAudioSource} is either valid 43 * (aka > 0, so successfully started) or hosting an error code (negative). 44 * 0 corresponds to an untialized or stopped HwAudioSource. 45 */ 46 private int mNativeHandle = 0; 47 48 /** 49 * Class constructor for a hardware audio source based player. 50 * 51 * Use the {@link Builder} class to construct a {@link HwAudioSource} instance. 52 * 53 * @param device {@link AudioDeviceInfo} instance of the source audio device. 54 * @param attributes {@link AudioAttributes} instance for this player. 55 */ HwAudioSource(@onNull AudioDeviceInfo device, @NonNull AudioAttributes attributes)56 private HwAudioSource(@NonNull AudioDeviceInfo device, @NonNull AudioAttributes attributes) { 57 super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_HW_SOURCE); 58 Preconditions.checkNotNull(device); 59 Preconditions.checkNotNull(attributes); 60 Preconditions.checkArgument(device.isSource(), "Requires a source device"); 61 mAudioDeviceInfo = device; 62 mAudioAttributes = attributes; 63 baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE); 64 } 65 66 /** 67 * TODO: sets the gain on {@link #mAudioDeviceInfo}. 68 * 69 * @param muting if true, the player is to be muted, and the volume values can be ignored 70 * @param leftVolume the left volume to use if muting is false 71 * @param rightVolume the right volume to use if muting is false 72 */ 73 @Override playerSetVolume(boolean muting, float leftVolume, float rightVolume)74 void playerSetVolume(boolean muting, float leftVolume, float rightVolume) { 75 } 76 77 /** 78 * TODO: applies {@link VolumeShaper} on {@link #mAudioDeviceInfo}. 79 * 80 * @param configuration a {@code VolumeShaper.Configuration} object 81 * created by {@link VolumeShaper.Configuration.Builder} or 82 * an created from a {@code VolumeShaper} id 83 * by the {@link VolumeShaper.Configuration} constructor. 84 * @param operation a {@code VolumeShaper.Operation}. 85 * @return 86 */ 87 @Override playerApplyVolumeShaper( @onNull VolumeShaper.Configuration configuration, @NonNull VolumeShaper.Operation operation)88 int playerApplyVolumeShaper( 89 @NonNull VolumeShaper.Configuration configuration, 90 @NonNull VolumeShaper.Operation operation) { 91 return 0; 92 } 93 94 /** 95 * TODO: gets the {@link VolumeShaper} by a given id. 96 * 97 * @param id the {@code VolumeShaper} id returned from 98 * sending a fully specified {@code VolumeShaper.Configuration} 99 * through {@link #playerApplyVolumeShaper} 100 * @return 101 */ 102 @Override 103 @Nullable playerGetVolumeShaperState(int id)104 VolumeShaper.State playerGetVolumeShaperState(int id) { 105 return new VolumeShaper.State(1f, 1f); 106 } 107 108 /** 109 * TODO: sets the level on {@link #mAudioDeviceInfo}. 110 * 111 * @param muting 112 * @param level 113 * @return 114 */ 115 @Override playerSetAuxEffectSendLevel(boolean muting, float level)116 int playerSetAuxEffectSendLevel(boolean muting, float level) { 117 return AudioSystem.SUCCESS; 118 } 119 120 @Override playerStart()121 void playerStart() { 122 start(); 123 } 124 125 @Override playerPause()126 void playerPause() { 127 // Pause is equivalent to stop for hardware audio source based players. 128 stop(); 129 } 130 131 @Override playerStop()132 void playerStop() { 133 stop(); 134 } 135 136 /** 137 * Starts the playback from {@link AudioDeviceInfo}. 138 * Starts does not return any error code, caller must check {@link HwAudioSource#isPlaying} to 139 * ensure the state of the HwAudioSource encoded in {@link mNativeHandle}. 140 */ start()141 public void start() { 142 Preconditions.checkState(!isPlaying(), "HwAudioSource is currently playing"); 143 mNativeHandle = AudioSystem.startAudioSource( 144 mAudioDeviceInfo.getPort().activeConfig(), 145 mAudioAttributes); 146 if (isPlaying()) { 147 // FIXME: b/174876389 clean up device id reporting 148 baseStart(getDeviceId()); 149 } 150 } 151 getDeviceId()152 private int getDeviceId() { 153 ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); 154 if (AudioManager.listAudioPatches(patches) != AudioManager.SUCCESS) { 155 return 0; 156 } 157 158 for (int i = 0; i < patches.size(); i++) { 159 AudioPatch patch = patches.get(i); 160 AudioPortConfig[] sources = patch.sources(); 161 AudioPortConfig[] sinks = patch.sinks(); 162 if ((sources != null) && (sources.length > 0)) { 163 for (int c = 0; c < sources.length; c++) { 164 if (sources[c].port().id() == mAudioDeviceInfo.getId()) { 165 return sinks[c].port().id(); 166 } 167 } 168 } 169 } 170 return 0; 171 } 172 173 /** 174 * Checks whether the HwAudioSource player is playing. 175 * It checks the state of the HwAudioSource encoded in {@link HwAudioSource#isPlaying}. 176 * 0 corresponds to a stopped or uninitialized HwAudioSource. 177 * Negative value corresponds to a status reported by {@link AudioSystem#startAudioSource} to 178 * indicate a failure when trying to start the HwAudioSource. 179 * 180 * @return true if currently playing, false otherwise 181 */ isPlaying()182 public boolean isPlaying() { 183 return mNativeHandle > 0; 184 } 185 186 /** 187 * Stops the playback from {@link AudioDeviceInfo}. 188 */ stop()189 public void stop() { 190 if (mNativeHandle > 0) { 191 baseStop(); 192 AudioSystem.stopAudioSource(mNativeHandle); 193 mNativeHandle = 0; 194 } 195 } 196 197 /** 198 * Builder class for {@link HwAudioSource} objects. 199 * Use this class to configure and create a <code>HwAudioSource</code> instance. 200 * <p>Here is an example where <code>Builder</code> is used to specify an audio 201 * playback directly from a source device as media usage, to be used by a new 202 * <code>HwAudioSource</code> instance: 203 * 204 * <pre class="prettyprint"> 205 * HwAudioSource player = new HwAudioSource.Builder() 206 * .setAudioAttributes(new AudioAttributes.Builder() 207 * .setUsage(AudioAttributes.USAGE_MEDIA) 208 * .build()) 209 * .setAudioDeviceInfo(device) 210 * .build() 211 * </pre> 212 * <p> 213 * If the audio attributes are not set with {@link #setAudioAttributes(AudioAttributes)}, 214 * attributes comprising {@link AudioAttributes#USAGE_MEDIA} will be used. 215 */ 216 public static final class Builder { 217 private AudioAttributes mAudioAttributes; 218 private AudioDeviceInfo mAudioDeviceInfo; 219 220 /** 221 * Constructs a new Builder with default values. 222 */ Builder()223 public Builder() { 224 } 225 226 /** 227 * Sets the {@link AudioAttributes}. 228 * @param attributes a non-null {@link AudioAttributes} instance that describes the audio 229 * data to be played. 230 * @return the same Builder instance. 231 */ setAudioAttributes(@onNull AudioAttributes attributes)232 public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes attributes) { 233 Preconditions.checkNotNull(attributes); 234 mAudioAttributes = attributes; 235 return this; 236 } 237 238 /** 239 * Sets the {@link AudioDeviceInfo}. 240 * @param info a non-null {@link AudioDeviceInfo} instance that describes the audio 241 * data come from. 242 * @return the same Builder instance. 243 */ setAudioDeviceInfo(@onNull AudioDeviceInfo info)244 public @NonNull Builder setAudioDeviceInfo(@NonNull AudioDeviceInfo info) { 245 Preconditions.checkNotNull(info); 246 Preconditions.checkArgument(info.isSource()); 247 mAudioDeviceInfo = info; 248 return this; 249 } 250 251 /** 252 * Builds an {@link HwAudioSource} instance initialized with all the parameters set 253 * on this <code>Builder</code>. 254 * @return a new successfully initialized {@link HwAudioSource} instance. 255 */ build()256 public @NonNull HwAudioSource build() { 257 Preconditions.checkNotNull(mAudioDeviceInfo); 258 if (mAudioAttributes == null) { 259 mAudioAttributes = new AudioAttributes.Builder() 260 .setUsage(AudioAttributes.USAGE_MEDIA) 261 .build(); 262 } 263 return new HwAudioSource(mAudioDeviceInfo, mAudioAttributes); 264 } 265 } 266 } 267