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