• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 package org.hyphonate.megaaudio.common;
17 
18 import android.content.Context;
19 import android.media.AudioFormat;
20 import android.media.AudioManager;
21 import android.util.Log;
22 
23 // For initialization
24 import org.hyphonate.megaaudio.player.JavaSourceProxy;
25 
26 /**
27  * Common base class for all audio streams.
28  */
29 public abstract class StreamBase {
30     @SuppressWarnings("unused")
31     private static final String TAG = StreamBase.class.getSimpleName();
32     @SuppressWarnings("unused")
33     private static final boolean LOG = true;
34 
35     static {
36         if (LOG) {
Log.d(TAG, "Loading MegaAudio Library...")37             Log.d(TAG, "Loading MegaAudio Library...");
38         }
39         try {
40             System.loadLibrary("megaaudio_jni");
JavaSourceProxy.initN()41             JavaSourceProxy.initN();
42         } catch (UnsatisfiedLinkError e) {
43             Log.e(TAG, "Error loading MegaAudio JNI library");
44             Log.e(TAG, "e: " + e);
45             e.printStackTrace();
46         }
47 
48         /* TODO: gracefully fail/notify if the library can't be loaded */
49     }
50 
51     //
52     // Error Codes
53     // These values must be kept in sync with the equivalent symbols in
54     // megaaudio/common/Streambase.h
55     //
56     public static final int OK = 0;
57     public static final int ERROR_UNKNOWN = -1;
58     public static final int ERROR_UNSUPPORTED = -2;
59     public static final int ERROR_INVALID_STATE = -3;
60     public static final int ERROR_DISCONNECTED = -899; // must match Oboe
61     public static final int ERROR_INVALIDSTATE = -895;
62 
63     //
64     // System Attributes
65     //
66     /**
67      * The size of the system "burst" buffer in frames.
68      * Note: Apps need to call calcNumBurstFrames(Context) to initialize this
69      * with the actual value for the system. 512 is an arbitrary, but safe value.
70      */
71     private static int sSystemBurstFrames = 512;
72 
73     /**
74      * The Preferred system sample rate.
75      */
76     private static int sSystemSampleRate = 48000;
77 
78     //
79     // Stream attributes
80     //
81     /**
82      * The number of channels in this stream.
83      */
84     protected int mChannelCount;
85 
86     /**
87      * The sample rate for this stream
88      */
89     protected int mSampleRate;
90 
91     /**
92      * The number of frames exchanged between the stream and the AudioSink/AudioSource.
93      * It is not (necessarily) the number of frames exchange with the OS player/recorder.
94      */
95     protected int mNumExchangeFrames;
96 
97     /**
98      * The performance mode for this stream.
99      * See Performance Mode Constants in Builder class.
100      */
101     protected int mPerformanceMode;
102 
103     /**
104      * The sharing mode for this stream. See Sharing Mode Constants in Builder class.
105      */
106     protected int mSharingMode;
107 
108     //TODO - Add methods for changing the routing of an instantiated stream.
109 
110     // the thread on which the underlying Android AudioTrack/AudioRecord will run
111     protected Thread mStreamThread = null;
112 
113     //
114     // Initialization
115     //
116 
117     /**
118      * Forces the load of the MegaAudio (native) library
119      */
loadMegaAudioLibrary()120     public static void loadMegaAudioLibrary() {
121         // NOP. This will force the static load
122     }
123 
124     /**
125      * Performs initialization. MUST be called before any Streams are created.
126      * @param context
127      */
setup(Context context)128     public static void setup(Context context) {
129         calcNumBurstFrames(context);
130         calcSystemSampleRate(context);
131     }
132 
133     //
134     // Attributes
135     //
136 
137     /**
138      * @return The number of channels associated with this stream.
139      */
getChannelCount()140     public int getChannelCount() { return mChannelCount; }
141 
142     /**
143      * @return The sample rate for this stream.
144      */
getSampleRate()145     public int getSampleRate() { return mSampleRate; }
146 
147     /**
148      * Gets the system-specified burst-size in frames. This should be called by the
149      * app in initialization before calling getSystemBurstFrames() (below).
150      * @return the system-specified burst size in frames.
151      */
calcNumBurstFrames(Context context)152     public static int calcNumBurstFrames(Context context) {
153         AudioManager audioManager = context.getSystemService(AudioManager.class);
154         String text = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
155         sSystemBurstFrames = Integer.parseInt(text);
156         if (LOG) {
157             Log.d(TAG, "sSystemBurstFrames:" + sSystemBurstFrames);
158         }
159         return sSystemBurstFrames;
160     }
161 
162     /**
163      * @return the system-specified burst size in frames.
164      */
getSystemBurstFrames()165     public static int getSystemBurstFrames() {
166         return sSystemBurstFrames;
167     }
168 
169     /**
170      * @param api Specifies which API BuilderBase.TYPE_NONE, BuilderBase.TYPE_JAVA
171      * or BuilderBase.TYPE_OBOE
172      * @return The optimal capacity for a stream buffer of the specified type.
173      */
getNumBurstFrames(int api)174     public static int getNumBurstFrames(int api) {
175         return sSystemBurstFrames;
176     }
177 
178     /**
179      *
180      */
getNumExchangeFrames()181     public int getNumExchangeFrames() {
182         return mNumExchangeFrames;
183     }
184 
185     /**
186      * Gets the system-speficied preferred sample rate for audio. This should be called by the
187      *      * app in initialization before calling getSystemSampleRate() (below).
188      * @return the system preferred sample rate
189      */
calcSystemSampleRate(Context context)190     public static int calcSystemSampleRate(Context context) {
191         AudioManager audioManager = context.getSystemService(AudioManager.class);
192         String text = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
193         return sSystemSampleRate = Integer.parseInt(text);
194     }
195 
196     /**
197      * @return the system preferred sample rate
198      */
getSystemSampleRate()199     public static int getSystemSampleRate() {
200         return sSystemSampleRate;
201     }
202 
203     // Routing
getRoutedDeviceId()204     public abstract int getRoutedDeviceId();
205 
206     //
207     // Sample Format Utils
208     //
209     /**
210      * @param encoding An Android ENCODING_ constant for audio data.
211      * @return The size in BYTES of samples encoded as specified.
212      */
sampleSizeInBytes(int encoding)213     public static int sampleSizeInBytes(int encoding) {
214         switch (encoding) {
215             case AudioFormat.ENCODING_PCM_16BIT:
216                 return 2;
217 
218             case AudioFormat.ENCODING_PCM_FLOAT:
219                 return 4;
220 
221             default:
222                 return 0;
223         }
224     }
225 
226     //
227     // State
228     //
229     /**
230      * Releases resources used by the stream.
231      * @return
232      */
teardownStream()233     public abstract int teardownStream();
234 
235     /**
236      * Starts playback on an open stream player. (@see open() method above).
237      * @return              ERROR_NONE if successful, otherwise an error code
238      */
startStream()239     public abstract int startStream();
240 
241     /**
242      * Stops playback.
243      * May not stop the stream immediately. i.e. does not stop until the next audio callback
244      * from the underlying system.
245      * @return              ERROR_NONE if successful, otherwise an error code
246      */
stopStream()247     public abstract int stopStream();
248 
249     /**
250      * @return See StreamState constants
251      */
getStreamState()252     public abstract int getStreamState();
253 
254     /**
255      * @return The last error callback result (these must match Oboe). See Oboe constants
256      */
getLastErrorCallbackResult()257     public abstract int getLastErrorCallbackResult();
258 
259     //
260     // Thread stuff
261     //
262     /**
263      * Joins the record thread to ensure that the stream is stopped.
264      */
waitForStreamThreadToExit()265     protected void waitForStreamThreadToExit() {
266         try {
267             if (mStreamThread != null) {
268                 mStreamThread.join();
269                 mStreamThread = null;
270             }
271         } catch (InterruptedException e) {
272             e.printStackTrace();
273         }
274     }
275 
276     //
277     // Utility
278     //
279     /**
280      * @param chanCount The number of channels for which to generate an index mask.
281      * @return  A channel index mask corresponding to the supplied channel count.
282      *
283      * note: The generated index mask has active channels from 0 to chanCount - 1
284      */
channelCountToIndexMask(int chanCount)285     public static int channelCountToIndexMask(int chanCount) {
286         return  (1 << chanCount) - 1;
287     }
288 
289     private static int[] sOutMasks =
290             {   -1,
291                 AudioFormat.CHANNEL_OUT_MONO,
292                 AudioFormat.CHANNEL_OUT_STEREO,
293                 AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER,
294                 AudioFormat.CHANNEL_OUT_QUAD
295             };
296 
297     /**
298      *
299      * @param chanCount The number of channels for which to generate a postional mask.
300      * @return the corresponding channel position mask
301      * note: This mapping is not well defined, but may be needed to get a fast path in the Java API
302      */
channelCountToOutPositionMask(int chanCount)303     public static int channelCountToOutPositionMask(int chanCount) {
304         return chanCount <= 4 ? sOutMasks[chanCount] : AudioFormat.CHANNEL_OUT_STEREO;
305     }
306 }
307