• 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_BAD_START = 4;
61     public static final int ERROR_BAD_OPEN = 5;
62     public static final int ERROR_INVALID_ARGUMENT = 6;
63     public static final int ERROR_DISCONNECTED = -899; // must match Oboe
64     public static final int ERROR_INVALIDSTATE = -895;
65 
66     //
67     // System Attributes
68     //
69     /**
70      * The size of the system "burst" buffer in frames.
71      * Note: Apps need to call calcNumBurstFrames(Context) to initialize this
72      * with the actual value for the system. 512 is an arbitrary, but safe value.
73      */
74     private static int sSystemBurstFrames = 512;
75 
76     /**
77      * The Preferred system sample rate.
78      */
79     private static int sSystemSampleRate = 48000;
80 
81     //
82     // Stream attributes
83     //
84     /**
85      * The number of channels requested for this stream.
86      */
87     protected int mChannelCount;
88 
89     protected int mChannelMask;
90 
91     /**
92      * The sample rate for this stream
93      */
94     protected int mSampleRate;
95 
96     /**
97      * The number of frames exchanged between the stream and the AudioSink/AudioSource.
98      * It is not (necessarily) the number of frames exchange with the OS player/recorder.
99      */
100     protected int mNumExchangeFrames;
101 
102     /**
103      * The performance mode for this stream.
104      * See Performance Mode Constants in Builder class.
105      */
106     protected int mPerformanceMode;
107 
108     /**
109      * The sharing mode for this stream. See Sharing Mode Constants in Builder class.
110      */
111     protected int mSharingMode;
112 
113     /**
114      * @return the sharing mode for the (open) stream
115      */
getSharingMode()116     public abstract int getSharingMode();
117 
118     //TODO - Add methods for changing the routing of an instantiated stream.
119 
120     // the thread on which the underlying Android AudioTrack/AudioRecord will run
121     protected Thread mStreamThread = null;
122 
123     //
124     // Initialization
125     //
126 
127     /**
128      * Forces the load of the MegaAudio (native) library
129      */
loadMegaAudioLibrary()130     public static void loadMegaAudioLibrary() {
131         // NOP. This will force the static load
132     }
133 
134     /**
135      * Performs initialization. MUST be called before any Streams are created.
136      * @param context
137      */
setup(Context context)138     public static void setup(Context context) {
139         calcNumBurstFrames(context);
140         calcSystemSampleRate(context);
141     }
142 
143     //
144     // Lifecycle
145     //
146     private static final int LIFECYCLE_NONE     = 0;
147     private static final int LIFECYCLE_BUILT    = 1;
148     private static final int LIFECYCLE_OPENED   = 2;
149     private static final int LIFECYCLE_STARTED  = 3;
150     private static final int LIFECYCLE_STOPPED  = 4;
151     private static final int LIFECYCLE_CLOSED   = 5;
152     private int mLifecycleStep = LIFECYCLE_NONE;
153 
154     /**
155      * Builds the stream, but does not open or start it.
156      *
157      * @param builder The builder containing the attributes for the stream.
158      * @return Either StreamBase.OK or an appropriate error code.
159      */
build(BuilderBase builder)160     public abstract int build(BuilderBase builder);
161 
162     /**
163      * Opens a stream in preparation for starting.
164      * @return Either StreamBase.OK or an appropriate error code.
165      */
open()166     public abstract int open();
167 
168     /**
169      * Starts the stream.
170      * @return Either StreamBase.OK or an appropriate error code.
171      */
start()172     public abstract int start();
173 
174     /**
175      * Stops the stream.
176      * @return Either StreamBase.OK or an appropriate error code.
177      */
stop()178     public abstract int stop();
179 
180     /**
181      * Closes the stream.
182      * @return Either StreamBase.OK or an appropriate error code.
183      */
close()184     public abstract int close();
185 
186     /**
187      * Tearsdown (deallocates) the stream.
188      * @return Either StreamBase.OK or an appropriate error code.
189      */
teardown()190     public abstract int teardown();
191 
trackBuild(int result)192     protected int trackBuild(int result) {
193         if (result == OK) {
194             mLifecycleStep = LIFECYCLE_BUILT;
195         }
196         return result;
197     }
198 
199     //
200     // Lifecycle Monitoring
201     //
trackOpen(int result)202     protected int trackOpen(int result) {
203         if (result == OK) {
204             mLifecycleStep = LIFECYCLE_OPENED;
205         }
206         return result;
207     }
208 
trackStart(int result)209     protected int trackStart(int result) {
210         if (result == OK) {
211             mLifecycleStep = LIFECYCLE_STARTED;
212         }
213         return result;
214     }
215 
trackStop(int result)216     protected int trackStop(int result) {
217         if (result == OK) {
218             mLifecycleStep = LIFECYCLE_STOPPED;
219         }
220         return result;
221     }
222 
trackClose(int result)223     protected int trackClose(int result) {
224         if (result == OK) {
225             mLifecycleStep = LIFECYCLE_CLOSED;
226         }
227         return result;
228     }
229 
trackTeardown(int result)230     protected int trackTeardown(int result) {
231         if (result == OK) {
232             mLifecycleStep = LIFECYCLE_NONE;
233         }
234         return result;
235     }
236 
237     /**
238      * stops/closes/tearsdown a stream based on its current lifecycle step.
239      */
unwind()240     public void unwind() {
241         if (LOG) {
242             Log.d(TAG, "unwind() mLifecycleStep: " + mLifecycleStep);
243         }
244         switch (mLifecycleStep) {
245             case LIFECYCLE_NONE:
246                 // NOP
247                 break;
248 
249             case LIFECYCLE_BUILT:
250                 teardown();
251                 break;
252 
253             case LIFECYCLE_OPENED:
254                 close();
255                 teardown();
256                 break;
257 
258             case LIFECYCLE_STARTED:
259                 stop();
260                 close();
261                 teardown();
262                 break;
263 
264             case LIFECYCLE_STOPPED:
265                 close();
266                 teardown();
267                 break;
268 
269             case LIFECYCLE_CLOSED:
270                 teardown();
271                 break;
272         }
273     }
274 
275     //
276     // Attributes
277     //
278 
279     /**
280      * @return The sample rate for this stream.
281      */
getSampleRate()282     public int getSampleRate() { return mSampleRate; }
283 
284     /**
285      * Gets the system-specified burst-size in frames. This should be called by the
286      * app in initialization before calling getSystemBurstFrames() (below).
287      * @return the system-specified burst size in frames.
288      */
calcNumBurstFrames(Context context)289     public static int calcNumBurstFrames(Context context) {
290         AudioManager audioManager = context.getSystemService(AudioManager.class);
291         String text = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
292         sSystemBurstFrames = Integer.parseInt(text);
293         if (LOG) {
294             Log.d(TAG, "sSystemBurstFrames:" + sSystemBurstFrames);
295         }
296         return sSystemBurstFrames;
297     }
298 
299     /**
300      * @return the system-specified burst size in frames.
301      */
getSystemBurstFrames()302     public static int getSystemBurstFrames() {
303         return sSystemBurstFrames;
304     }
305 
306     /**
307      * @param api Specifies which API BuilderBase.TYPE_NONE, BuilderBase.TYPE_JAVA
308      * or BuilderBase.TYPE_OBOE
309      * @return The optimal capacity for a stream buffer of the specified type.
310      */
getNumBurstFrames(int api)311     public static int getNumBurstFrames(int api) {
312         return sSystemBurstFrames;
313     }
314 
315     /**
316      *
317      */
getNumExchangeFrames()318     public int getNumExchangeFrames() {
319         return mNumExchangeFrames;
320     }
321 
322     /**
323      * Gets the system-speficied preferred sample rate for audio. This should be called by the
324      *      * app in initialization before calling getSystemSampleRate() (below).
325      * @return the system preferred sample rate
326      */
calcSystemSampleRate(Context context)327     public static int calcSystemSampleRate(Context context) {
328         AudioManager audioManager = context.getSystemService(AudioManager.class);
329         String text = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
330         return sSystemSampleRate = Integer.parseInt(text);
331     }
332 
333     /**
334      * @return the system preferred sample rate
335      */
getSystemSampleRate()336     public static int getSystemSampleRate() {
337         return sSystemSampleRate;
338     }
339 
340     // Routing
getRoutedDeviceId()341     public abstract int getRoutedDeviceId();
342 
343     //
344     // Sample Format Utils
345     //
346     /**
347      * @param encoding An Android ENCODING_ constant for audio data.
348      * @return The size in BYTES of samples encoded as specified.
349      */
sampleSizeInBytes(int encoding)350     public static int sampleSizeInBytes(int encoding) {
351         switch (encoding) {
352             case AudioFormat.ENCODING_PCM_16BIT:
353                 return 2;
354 
355             case AudioFormat.ENCODING_PCM_FLOAT:
356                 return 4;
357 
358             default:
359                 return 0;
360         }
361     }
362 
363     //
364     // State
365     //
366     /**
367      * @return See StreamState constants
368      */
getStreamState()369     public abstract int getStreamState();
370 
371     /**
372      * @return the ACTUAL number of channels in this stream
373      * (as opposed to the number requested).
374      * -1 if there is no valid stream.
375      */
getChannelCount()376     public abstract int getChannelCount();
377 
378     /**
379      * Note: The stream must be created before calling this method.
380      * @return true if the underlying stream is an MMAP stream, false otherwise.
381      */
isMMap()382     public abstract boolean isMMap();
383 
384     /**
385      * @return The last error callback result (these must match Oboe). See Oboe constants
386      */
getLastErrorCallbackResult()387     public abstract int getLastErrorCallbackResult();
388 
389     //
390     // Thread stuff
391     //
392     /**
393      * Joins the record thread to ensure that the stream is stopped.
394      */
waitForStreamThreadToExit()395     protected void waitForStreamThreadToExit() {
396         try {
397             if (mStreamThread != null) {
398                 mStreamThread.join();
399                 mStreamThread = null;
400             }
401         } catch (InterruptedException e) {
402             e.printStackTrace();
403         }
404     }
405 
406     //
407     // Utility
408     //
409     /**
410      * @param chanCount The number of channels for which to generate an index mask.
411      * @return  A channel index mask corresponding to the supplied channel count.
412      *
413      * note: The generated index mask has active channels from 0 to chanCount - 1
414      */
channelCountToIndexMask(int chanCount)415     public static int channelCountToIndexMask(int chanCount) {
416         return  (1 << chanCount) - 1;
417     }
418 
419     private static int[] sOutMasks =
420             {   -1,
421                 AudioFormat.CHANNEL_OUT_MONO,
422                 AudioFormat.CHANNEL_OUT_STEREO,
423                 AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER,
424                 AudioFormat.CHANNEL_OUT_QUAD
425             };
426 
427     /**
428      *
429      * @param chanCount The number of channels for which to generate a postional mask.
430      * @return the corresponding channel position mask
431      * note: This mapping is not well defined, but may be needed to get a fast path in the Java API
432      */
channelCountToOutPositionMask(int chanCount)433     public static int channelCountToOutPositionMask(int chanCount) {
434         return chanCount <= 4 ? sOutMasks[chanCount] : AudioFormat.CHANNEL_OUT_STEREO;
435     }
436 }
437