Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Separated out SDL Android java code so audio, controller, and filesys…
…tem APIs can be used independently of the SDL activity, in Qt apps for example.
- Loading branch information
Showing
3 changed files
with
643 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package org.libsdl.app; | ||
|
||
import android.content.Context; | ||
|
||
/** | ||
SDL library initialization | ||
*/ | ||
public class SDL { | ||
|
||
// This function should be called first and sets up the native code | ||
// so it can call into the Java classes | ||
public static void setupJNI() { | ||
SDLActivity.nativeSetupJNI(); | ||
SDLAudioManager.nativeSetupJNI(); | ||
SDLControllerManager.nativeSetupJNI(); | ||
} | ||
|
||
// This function should be called each time the activity is started | ||
public static void initialize() { | ||
setContext(null); | ||
|
||
SDLActivity.initialize(); | ||
SDLAudioManager.initialize(); | ||
SDLControllerManager.initialize(); | ||
} | ||
|
||
// This function stores the current activity (SDL or not) | ||
public static void setContext(Context context) { | ||
mContext = context; | ||
} | ||
|
||
public static Context getContext() { | ||
return mContext; | ||
} | ||
|
||
protected static Context mContext; | ||
} |
179 changes: 179 additions & 0 deletions
179
android-project/src/org/libsdl/app/SDLAudioManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
package org.libsdl.app; | ||
|
||
import android.media.*; | ||
import android.hardware.*; | ||
import android.util.Log; | ||
|
||
public class SDLAudioManager | ||
{ | ||
protected static final String TAG = "SDLAudio"; | ||
|
||
protected static AudioTrack mAudioTrack; | ||
protected static AudioRecord mAudioRecord; | ||
|
||
public static void initialize() { | ||
mAudioTrack = null; | ||
mAudioRecord = null; | ||
} | ||
|
||
// Audio | ||
|
||
/** | ||
* This method is called by SDL using JNI. | ||
*/ | ||
public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { | ||
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; | ||
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; | ||
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); | ||
|
||
Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); | ||
|
||
// Let the user pick a larger buffer if they really want -- but ye | ||
// gods they probably shouldn't, the minimums are horrifyingly high | ||
// latency already | ||
desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); | ||
|
||
if (mAudioTrack == null) { | ||
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, | ||
channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); | ||
|
||
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid | ||
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java | ||
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState() | ||
|
||
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) { | ||
Log.e(TAG, "Failed during initialization of Audio Track"); | ||
mAudioTrack = null; | ||
return -1; | ||
} | ||
|
||
mAudioTrack.play(); | ||
} | ||
|
||
Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); | ||
|
||
return 0; | ||
} | ||
|
||
/** | ||
* This method is called by SDL using JNI. | ||
*/ | ||
public static void audioWriteShortBuffer(short[] buffer) { | ||
if (mAudioTrack == null) { | ||
Log.e(TAG, "Attempted to make audio call with uninitialized audio!"); | ||
return; | ||
} | ||
|
||
for (int i = 0; i < buffer.length; ) { | ||
int result = mAudioTrack.write(buffer, i, buffer.length - i); | ||
if (result > 0) { | ||
i += result; | ||
} else if (result == 0) { | ||
try { | ||
Thread.sleep(1); | ||
} catch(InterruptedException e) { | ||
// Nom nom | ||
} | ||
} else { | ||
Log.w(TAG, "SDL audio: error return from write(short)"); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* This method is called by SDL using JNI. | ||
*/ | ||
public static void audioWriteByteBuffer(byte[] buffer) { | ||
if (mAudioTrack == null) { | ||
Log.e(TAG, "Attempted to make audio call with uninitialized audio!"); | ||
return; | ||
} | ||
|
||
for (int i = 0; i < buffer.length; ) { | ||
int result = mAudioTrack.write(buffer, i, buffer.length - i); | ||
if (result > 0) { | ||
i += result; | ||
} else if (result == 0) { | ||
try { | ||
Thread.sleep(1); | ||
} catch(InterruptedException e) { | ||
// Nom nom | ||
} | ||
} else { | ||
Log.w(TAG, "SDL audio: error return from write(byte)"); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* This method is called by SDL using JNI. | ||
*/ | ||
public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { | ||
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; | ||
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; | ||
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); | ||
|
||
Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); | ||
|
||
// Let the user pick a larger buffer if they really want -- but ye | ||
// gods they probably shouldn't, the minimums are horrifyingly high | ||
// latency already | ||
desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); | ||
|
||
if (mAudioRecord == null) { | ||
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, | ||
channelConfig, audioFormat, desiredFrames * frameSize); | ||
|
||
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here. | ||
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) { | ||
Log.e(TAG, "Failed during initialization of AudioRecord"); | ||
mAudioRecord.release(); | ||
mAudioRecord = null; | ||
return -1; | ||
} | ||
|
||
mAudioRecord.startRecording(); | ||
} | ||
|
||
Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); | ||
|
||
return 0; | ||
} | ||
|
||
/** This method is called by SDL using JNI. */ | ||
public static int captureReadShortBuffer(short[] buffer, boolean blocking) { | ||
// !!! FIXME: this is available in API Level 23. Until then, we always block. :( | ||
//return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); | ||
return mAudioRecord.read(buffer, 0, buffer.length); | ||
} | ||
|
||
/** This method is called by SDL using JNI. */ | ||
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) { | ||
// !!! FIXME: this is available in API Level 23. Until then, we always block. :( | ||
//return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); | ||
return mAudioRecord.read(buffer, 0, buffer.length); | ||
} | ||
|
||
|
||
/** This method is called by SDL using JNI. */ | ||
public static void audioClose() { | ||
if (mAudioTrack != null) { | ||
mAudioTrack.stop(); | ||
mAudioTrack.release(); | ||
mAudioTrack = null; | ||
} | ||
} | ||
|
||
/** This method is called by SDL using JNI. */ | ||
public static void captureClose() { | ||
if (mAudioRecord != null) { | ||
mAudioRecord.stop(); | ||
mAudioRecord.release(); | ||
mAudioRecord = null; | ||
} | ||
} | ||
|
||
public static native int nativeSetupJNI(); | ||
} |
Oops, something went wrong.