mirror of
https://github.com/Relintai/sdl2_frt.git
synced 2025-01-22 01:57:18 +01:00
Added support for surround sound and float audio on Android
This commit is contained in:
parent
4679f6826d
commit
f5a21ebf0c
@ -1,6 +1,7 @@
|
|||||||
package org.libsdl.app;
|
package org.libsdl.app;
|
||||||
|
|
||||||
import android.media.*;
|
import android.media.*;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class SDLAudioManager
|
public class SDLAudioManager
|
||||||
@ -17,41 +18,250 @@ public class SDLAudioManager
|
|||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
|
|
||||||
/**
|
protected static String getAudioFormatString(int audioFormat) {
|
||||||
* This method is called by SDL using JNI.
|
switch (audioFormat) {
|
||||||
*/
|
case AudioFormat.ENCODING_PCM_8BIT:
|
||||||
public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
|
return "8-bit";
|
||||||
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
|
case AudioFormat.ENCODING_PCM_16BIT:
|
||||||
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
|
return "16-bit";
|
||||||
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
|
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||||
|
return "float";
|
||||||
|
default:
|
||||||
|
return Integer.toString(audioFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
|
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
|
||||||
|
int channelConfig;
|
||||||
|
int sampleSize;
|
||||||
|
int frameSize;
|
||||||
|
|
||||||
|
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
|
||||||
|
|
||||||
|
/* On older devices let's use known good settings */
|
||||||
|
if (Build.VERSION.SDK_INT < 21) {
|
||||||
|
if (desiredChannels > 2) {
|
||||||
|
desiredChannels = 2;
|
||||||
|
}
|
||||||
|
if (sampleRate < 8000) {
|
||||||
|
sampleRate = 8000;
|
||||||
|
} else if (sampleRate > 48000) {
|
||||||
|
sampleRate = 48000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
|
||||||
|
int minSDKVersion = (isCapture ? 23 : 21);
|
||||||
|
if (Build.VERSION.SDK_INT < minSDKVersion) {
|
||||||
|
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (audioFormat)
|
||||||
|
{
|
||||||
|
case AudioFormat.ENCODING_PCM_8BIT:
|
||||||
|
sampleSize = 1;
|
||||||
|
break;
|
||||||
|
case AudioFormat.ENCODING_PCM_16BIT:
|
||||||
|
sampleSize = 2;
|
||||||
|
break;
|
||||||
|
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||||
|
sampleSize = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
|
||||||
|
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
|
sampleSize = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCapture) {
|
||||||
|
switch (desiredChannels) {
|
||||||
|
case 1:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||||
|
desiredChannels = 2;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (desiredChannels) {
|
||||||
|
case 1:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
|
||||||
|
desiredChannels = 6;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||||
|
desiredChannels = 2;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Log.v(TAG, "Speaker configuration (and order of channels):");
|
||||||
|
|
||||||
|
if ((channelConfig & 0x00000004) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000008) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000010) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000020) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000040) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000080) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000100) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000200) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000400) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000800) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00001000) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
frameSize = (sampleSize * desiredChannels);
|
||||||
|
|
||||||
// Let the user pick a larger buffer if they really want -- but ye
|
// Let the user pick a larger buffer if they really want -- but ye
|
||||||
// gods they probably shouldn't, the minimums are horrifyingly high
|
// gods they probably shouldn't, the minimums are horrifyingly high
|
||||||
// latency already
|
// latency already
|
||||||
desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
|
int minBufferSize;
|
||||||
|
if (isCapture) {
|
||||||
|
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||||
|
} else {
|
||||||
|
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||||
|
}
|
||||||
|
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
|
||||||
|
|
||||||
if (mAudioTrack == null) {
|
int[] results = new int[4];
|
||||||
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
|
if (isCapture) {
|
||||||
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
|
if (mAudioRecord == null) {
|
||||||
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
|
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
|
||||||
|
channelConfig, audioFormat, desiredFrames * frameSize);
|
||||||
|
|
||||||
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
|
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
|
||||||
Log.e(TAG, "Failed during initialization of Audio Track");
|
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
|
||||||
mAudioTrack = null;
|
Log.e(TAG, "Failed during initialization of AudioRecord");
|
||||||
return -1;
|
mAudioRecord.release();
|
||||||
|
mAudioRecord = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioRecord.startRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
mAudioTrack.play();
|
results[0] = mAudioRecord.getSampleRate();
|
||||||
|
results[1] = mAudioRecord.getAudioFormat();
|
||||||
|
results[2] = mAudioRecord.getChannelCount();
|
||||||
|
results[3] = desiredFrames;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
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) {
|
||||||
|
/* Try again, with safer values */
|
||||||
|
|
||||||
|
Log.e(TAG, "Failed during initialization of Audio Track");
|
||||||
|
mAudioTrack.release();
|
||||||
|
mAudioTrack = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioTrack.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
results[0] = mAudioTrack.getSampleRate();
|
||||||
|
results[1] = mAudioTrack.getAudioFormat();
|
||||||
|
results[2] = mAudioTrack.getChannelCount();
|
||||||
|
results[3] = desiredFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
|
||||||
|
|
||||||
return 0;
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
|
||||||
|
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteFloatBuffer(float[] 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, AudioTrack.WRITE_BLOCKING);
|
||||||
|
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(float)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,7 +273,7 @@ public class SDLAudioManager
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < buffer.length; ) {
|
for (int i = 0; i < buffer.length;) {
|
||||||
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
i += result;
|
i += result;
|
||||||
@ -109,53 +319,33 @@ public class SDLAudioManager
|
|||||||
/**
|
/**
|
||||||
* This method is called by SDL using JNI.
|
* This method is called by SDL using JNI.
|
||||||
*/
|
*/
|
||||||
public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
|
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
|
||||||
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
|
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
|
||||||
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");
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
|
||||||
// Let the user pick a larger buffer if they really want -- but ye
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
// 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. */
|
/** This method is called by SDL using JNI. */
|
||||||
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
|
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
|
||||||
// !!! FIXME: this is available in API Level 23. Until then, we always block. :(
|
if (Build.VERSION.SDK_INT < 23) {
|
||||||
//return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||||
return mAudioRecord.read(buffer, 0, buffer.length);
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This method is called by SDL using JNI. */
|
/** This method is called by SDL using JNI. */
|
||||||
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
|
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
|
||||||
// !!! FIXME: this is available in API Level 23. Until then, we always block. :(
|
if (Build.VERSION.SDK_INT < 23) {
|
||||||
//return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||||
return mAudioRecord.read(buffer, 0, buffer.length);
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** This method is called by SDL using JNI. */
|
/** This method is called by SDL using JNI. */
|
||||||
public static void audioClose() {
|
public static void audioClose() {
|
||||||
if (mAudioTrack != null) {
|
if (mAudioTrack != null) {
|
||||||
|
@ -1130,8 +1130,9 @@ prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared)
|
|||||||
}
|
}
|
||||||
case 1: /* Mono */
|
case 1: /* Mono */
|
||||||
case 2: /* Stereo */
|
case 2: /* Stereo */
|
||||||
case 4: /* surround */
|
case 4: /* Quadrophonic */
|
||||||
case 6: /* surround with center and lfe */
|
case 6: /* 5.1 surround */
|
||||||
|
case 8: /* 7.1 surround */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
SDL_SetError("Unsupported number of audio channels.");
|
SDL_SetError("Unsupported number of audio channels.");
|
||||||
|
@ -57,7 +57,9 @@ ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
|||||||
|
|
||||||
test_format = SDL_FirstAudioFormat(this->spec.format);
|
test_format = SDL_FirstAudioFormat(this->spec.format);
|
||||||
while (test_format != 0) { /* no "UNKNOWN" constant */
|
while (test_format != 0) { /* no "UNKNOWN" constant */
|
||||||
if ((test_format == AUDIO_U8) || (test_format == AUDIO_S16LSB)) {
|
if ((test_format == AUDIO_U8) ||
|
||||||
|
(test_format == AUDIO_S16) ||
|
||||||
|
(test_format == AUDIO_F32)) {
|
||||||
this->spec.format = test_format;
|
this->spec.format = test_format;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -69,25 +71,8 @@ ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
|||||||
return SDL_SetError("No compatible audio format!");
|
return SDL_SetError("No compatible audio format!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->spec.channels > 1) {
|
if (Android_JNI_OpenAudioDevice(iscapture, &this->spec) < 0) {
|
||||||
this->spec.channels = 2;
|
return -1;
|
||||||
} else {
|
|
||||||
this->spec.channels = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->spec.freq < 8000) {
|
|
||||||
this->spec.freq = 8000;
|
|
||||||
}
|
|
||||||
if (this->spec.freq > 48000) {
|
|
||||||
this->spec.freq = 48000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: pass in/return a (Java) device ID */
|
|
||||||
this->spec.samples = Android_JNI_OpenAudioDevice(iscapture, this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
|
|
||||||
|
|
||||||
if (this->spec.samples == 0) {
|
|
||||||
/* Init failed? */
|
|
||||||
return SDL_SetError("Java-side initialization failed!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_CalculateAudioSpec(&this->spec);
|
SDL_CalculateAudioSpec(&this->spec);
|
||||||
|
@ -61,6 +61,10 @@
|
|||||||
#define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
|
#define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
|
||||||
#define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
|
#define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
|
||||||
|
|
||||||
|
/* Audio encoding definitions */
|
||||||
|
#define ENCODING_PCM_8BIT 3
|
||||||
|
#define ENCODING_PCM_16BIT 2
|
||||||
|
#define ENCODING_PCM_FLOAT 4
|
||||||
|
|
||||||
/* Java class SDLActivity */
|
/* Java class SDLActivity */
|
||||||
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
|
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
|
||||||
@ -248,12 +252,14 @@ static jclass mAudioManagerClass;
|
|||||||
|
|
||||||
/* method signatures */
|
/* method signatures */
|
||||||
static jmethodID midAudioOpen;
|
static jmethodID midAudioOpen;
|
||||||
static jmethodID midAudioWriteShortBuffer;
|
|
||||||
static jmethodID midAudioWriteByteBuffer;
|
static jmethodID midAudioWriteByteBuffer;
|
||||||
|
static jmethodID midAudioWriteShortBuffer;
|
||||||
|
static jmethodID midAudioWriteFloatBuffer;
|
||||||
static jmethodID midAudioClose;
|
static jmethodID midAudioClose;
|
||||||
static jmethodID midCaptureOpen;
|
static jmethodID midCaptureOpen;
|
||||||
static jmethodID midCaptureReadShortBuffer;
|
|
||||||
static jmethodID midCaptureReadByteBuffer;
|
static jmethodID midCaptureReadByteBuffer;
|
||||||
|
static jmethodID midCaptureReadShortBuffer;
|
||||||
|
static jmethodID midCaptureReadFloatBuffer;
|
||||||
static jmethodID midCaptureClose;
|
static jmethodID midCaptureClose;
|
||||||
|
|
||||||
/* controller manager */
|
/* controller manager */
|
||||||
@ -397,24 +403,28 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jc
|
|||||||
mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
|
mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
|
||||||
|
|
||||||
midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
"audioOpen", "(IZZI)I");
|
"audioOpen", "(IIII)[I");
|
||||||
midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
|
||||||
"audioWriteShortBuffer", "([S)V");
|
|
||||||
midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
"audioWriteByteBuffer", "([B)V");
|
"audioWriteByteBuffer", "([B)V");
|
||||||
|
midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
|
"audioWriteShortBuffer", "([S)V");
|
||||||
|
midAudioWriteFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
|
"audioWriteFloatBuffer", "([F)V");
|
||||||
midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
"audioClose", "()V");
|
"audioClose", "()V");
|
||||||
midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
"captureOpen", "(IZZI)I");
|
"captureOpen", "(IIII)[I");
|
||||||
midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
|
||||||
"captureReadShortBuffer", "([SZ)I");
|
|
||||||
midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
"captureReadByteBuffer", "([BZ)I");
|
"captureReadByteBuffer", "([BZ)I");
|
||||||
|
midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
|
"captureReadShortBuffer", "([SZ)I");
|
||||||
|
midCaptureReadFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
|
"captureReadFloatBuffer", "([FZ)I");
|
||||||
midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
|
||||||
"captureClose", "()V");
|
"captureClose", "()V");
|
||||||
|
|
||||||
if (!midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
|
if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
|
||||||
!midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose) {
|
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose) {
|
||||||
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
|
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1043,17 +1053,19 @@ int Android_JNI_SetupThread(void)
|
|||||||
/*
|
/*
|
||||||
* Audio support
|
* Audio support
|
||||||
*/
|
*/
|
||||||
static jboolean audioBuffer16Bit = JNI_FALSE;
|
static int audioBufferFormat = 0;
|
||||||
static jobject audioBuffer = NULL;
|
static jobject audioBuffer = NULL;
|
||||||
static void* audioBufferPinned = NULL;
|
static void* audioBufferPinned = NULL;
|
||||||
static jboolean captureBuffer16Bit = JNI_FALSE;
|
static int captureBufferFormat = 0;
|
||||||
static jobject captureBuffer = NULL;
|
static jobject captureBuffer = NULL;
|
||||||
|
|
||||||
int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
|
int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec)
|
||||||
{
|
{
|
||||||
jboolean isStereo;
|
int audioformat;
|
||||||
int numBufferFrames;
|
int numBufferFrames;
|
||||||
jobject jbufobj = NULL;
|
jobject jbufobj = NULL;
|
||||||
|
jobject result;
|
||||||
|
int *resultElements;
|
||||||
jboolean isCopy;
|
jboolean isCopy;
|
||||||
|
|
||||||
JNIEnv *env = Android_JNI_GetEnv();
|
JNIEnv *env = Android_JNI_GetEnv();
|
||||||
@ -1063,78 +1075,123 @@ int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int
|
|||||||
}
|
}
|
||||||
Android_JNI_SetupThread();
|
Android_JNI_SetupThread();
|
||||||
|
|
||||||
isStereo = channelCount > 1;
|
switch (spec->format) {
|
||||||
|
case AUDIO_U8:
|
||||||
|
audioformat = ENCODING_PCM_8BIT;
|
||||||
|
break;
|
||||||
|
case AUDIO_S16:
|
||||||
|
audioformat = ENCODING_PCM_16BIT;
|
||||||
|
break;
|
||||||
|
case AUDIO_F32:
|
||||||
|
audioformat = ENCODING_PCM_FLOAT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SDL_SetError("Unsupported audio format: 0x%x", spec->format);
|
||||||
|
}
|
||||||
|
|
||||||
if (iscapture) {
|
if (iscapture) {
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
|
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
|
||||||
captureBuffer16Bit = is16Bit;
|
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples);
|
||||||
if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureOpen, sampleRate, is16Bit, isStereo, desiredBufferFrames) != 0) {
|
|
||||||
/* Error during audio initialization */
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
|
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
|
||||||
audioBuffer16Bit = is16Bit;
|
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples);
|
||||||
if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midAudioOpen, sampleRate, is16Bit, isStereo, desiredBufferFrames) != 0) {
|
|
||||||
/* Error during audio initialization */
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (result == NULL) {
|
||||||
|
/* Error during audio initialization, error printed from Java */
|
||||||
|
return SDL_SetError("Java-side initialization failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*env)->GetArrayLength(env, (jintArray)result) != 4) {
|
||||||
|
return SDL_SetError("Unexpected results from Java, expected 4, got %d", (*env)->GetArrayLength(env, (jintArray)result));
|
||||||
|
}
|
||||||
|
isCopy = JNI_FALSE;
|
||||||
|
resultElements = (*env)->GetIntArrayElements(env, (jintArray)result, &isCopy);
|
||||||
|
spec->freq = resultElements[0];
|
||||||
|
audioformat = resultElements[1];
|
||||||
|
switch (audioformat) {
|
||||||
|
case ENCODING_PCM_8BIT:
|
||||||
|
spec->format = AUDIO_U8;
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_16BIT:
|
||||||
|
spec->format = AUDIO_S16;
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_FLOAT:
|
||||||
|
spec->format = AUDIO_F32;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
|
||||||
|
}
|
||||||
|
spec->channels = resultElements[2];
|
||||||
|
spec->samples = resultElements[3];
|
||||||
|
(*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
|
||||||
|
(*env)->DeleteLocalRef(env, result);
|
||||||
|
|
||||||
/* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
|
/* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
|
||||||
* Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
|
* Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
|
||||||
|
switch (audioformat) {
|
||||||
if (is16Bit) {
|
case ENCODING_PCM_8BIT:
|
||||||
jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (isStereo ? 2 : 1));
|
{
|
||||||
if (audioBufferLocal) {
|
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels);
|
||||||
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
if (audioBufferLocal) {
|
||||||
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||||
|
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
else {
|
case ENCODING_PCM_16BIT:
|
||||||
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (isStereo ? 2 : 1));
|
{
|
||||||
if (audioBufferLocal) {
|
jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels);
|
||||||
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
if (audioBufferLocal) {
|
||||||
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||||
|
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_FLOAT:
|
||||||
|
{
|
||||||
|
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels);
|
||||||
|
if (audioBufferLocal) {
|
||||||
|
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||||
|
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jbufobj == NULL) {
|
if (jbufobj == NULL) {
|
||||||
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
|
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer");
|
||||||
return 0;
|
return SDL_OutOfMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iscapture) {
|
if (iscapture) {
|
||||||
|
captureBufferFormat = audioformat;
|
||||||
captureBuffer = jbufobj;
|
captureBuffer = jbufobj;
|
||||||
} else {
|
} else {
|
||||||
|
audioBufferFormat = audioformat;
|
||||||
audioBuffer = jbufobj;
|
audioBuffer = jbufobj;
|
||||||
}
|
}
|
||||||
|
numBufferFrames = (*env)->GetArrayLength(env, (jarray)jbufobj);
|
||||||
|
|
||||||
isCopy = JNI_FALSE;
|
if (!iscapture) {
|
||||||
|
isCopy = JNI_FALSE;
|
||||||
|
|
||||||
if (is16Bit) {
|
switch (audioformat) {
|
||||||
if (iscapture) {
|
case ENCODING_PCM_8BIT:
|
||||||
numBufferFrames = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
|
|
||||||
} else {
|
|
||||||
audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
|
|
||||||
numBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (iscapture) {
|
|
||||||
numBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
|
|
||||||
} else {
|
|
||||||
audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
|
audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
|
||||||
numBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
|
break;
|
||||||
|
case ENCODING_PCM_16BIT:
|
||||||
|
audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_FLOAT:
|
||||||
|
audioBufferPinned = (*env)->GetFloatArrayElements(env, (jfloatArray)audioBuffer, &isCopy);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
if (isStereo) {
|
|
||||||
numBufferFrames /= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return numBufferFrames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi)
|
int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi)
|
||||||
@ -1178,12 +1235,22 @@ void Android_JNI_WriteAudioBuffer(void)
|
|||||||
{
|
{
|
||||||
JNIEnv *mAudioEnv = Android_JNI_GetEnv();
|
JNIEnv *mAudioEnv = Android_JNI_GetEnv();
|
||||||
|
|
||||||
if (audioBuffer16Bit) {
|
switch (audioBufferFormat) {
|
||||||
(*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
|
case ENCODING_PCM_8BIT:
|
||||||
(*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
|
|
||||||
} else {
|
|
||||||
(*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
|
(*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
|
||||||
(*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
|
(*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_16BIT:
|
||||||
|
(*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
|
||||||
|
(*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_FLOAT:
|
||||||
|
(*mAudioEnv)->ReleaseFloatArrayElements(mAudioEnv, (jfloatArray)audioBuffer, (jfloat *)audioBufferPinned, JNI_COMMIT);
|
||||||
|
(*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteFloatBuffer, (jfloatArray)audioBuffer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled audio buffer format");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
|
/* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
|
||||||
@ -1195,16 +1262,8 @@ int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
|
|||||||
jboolean isCopy = JNI_FALSE;
|
jboolean isCopy = JNI_FALSE;
|
||||||
jint br;
|
jint br;
|
||||||
|
|
||||||
if (captureBuffer16Bit) {
|
switch (captureBufferFormat) {
|
||||||
SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / 2));
|
case ENCODING_PCM_8BIT:
|
||||||
br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
|
|
||||||
if (br > 0) {
|
|
||||||
jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
|
|
||||||
br *= 2;
|
|
||||||
SDL_memcpy(buffer, ptr, br);
|
|
||||||
(*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
|
SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
|
||||||
br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
|
br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
|
||||||
if (br > 0) {
|
if (br > 0) {
|
||||||
@ -1212,27 +1271,75 @@ int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
|
|||||||
SDL_memcpy(buffer, ptr, br);
|
SDL_memcpy(buffer, ptr, br);
|
||||||
(*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT);
|
(*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_16BIT:
|
||||||
|
SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / sizeof(Sint16)));
|
||||||
|
br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
|
||||||
|
if (br > 0) {
|
||||||
|
jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
|
||||||
|
br *= sizeof(Sint16);
|
||||||
|
SDL_memcpy(buffer, ptr, br);
|
||||||
|
(*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_FLOAT:
|
||||||
|
SDL_assert((*env)->GetArrayLength(env, (jfloatArray)captureBuffer) == (buflen / sizeof(float)));
|
||||||
|
br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_TRUE);
|
||||||
|
if (br > 0) {
|
||||||
|
jfloat *ptr = (*env)->GetFloatArrayElements(env, (jfloatArray)captureBuffer, &isCopy);
|
||||||
|
br *= sizeof(float);
|
||||||
|
SDL_memcpy(buffer, ptr, br);
|
||||||
|
(*env)->ReleaseFloatArrayElements(env, (jfloatArray)captureBuffer, (jfloat *)ptr, JNI_ABORT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled capture buffer format");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
return br;
|
||||||
return (int) br;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Android_JNI_FlushCapturedAudio(void)
|
void Android_JNI_FlushCapturedAudio(void)
|
||||||
{
|
{
|
||||||
JNIEnv *env = Android_JNI_GetEnv();
|
JNIEnv *env = Android_JNI_GetEnv();
|
||||||
#if 0 /* !!! FIXME: this needs API 23, or it'll do blocking reads and never end. */
|
#if 0 /* !!! FIXME: this needs API 23, or it'll do blocking reads and never end. */
|
||||||
if (captureBuffer16Bit) {
|
switch (captureBufferFormat) {
|
||||||
const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
|
case ENCODING_PCM_8BIT:
|
||||||
while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
|
{
|
||||||
} else {
|
const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
|
||||||
const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
|
while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
|
||||||
while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
|
}
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_16BIT:
|
||||||
|
{
|
||||||
|
const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
|
||||||
|
while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_FLOAT:
|
||||||
|
{
|
||||||
|
const jint len = (*env)->GetArrayLength(env, (jfloatArray)captureBuffer);
|
||||||
|
while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: flushing unhandled capture buffer format");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (captureBuffer16Bit) {
|
switch (captureBufferFormat) {
|
||||||
(*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
|
case ENCODING_PCM_8BIT:
|
||||||
} else {
|
|
||||||
(*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
|
(*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_16BIT:
|
||||||
|
(*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
|
||||||
|
break;
|
||||||
|
case ENCODING_PCM_FLOAT:
|
||||||
|
(*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: flushing unhandled capture buffer format");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ extern "C" {
|
|||||||
#include <EGL/eglplatform.h>
|
#include <EGL/eglplatform.h>
|
||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
|
|
||||||
|
#include "SDL_audio.h"
|
||||||
#include "SDL_rect.h"
|
#include "SDL_rect.h"
|
||||||
|
|
||||||
/* Interface from the SDL library into the Android Java activity */
|
/* Interface from the SDL library into the Android Java activity */
|
||||||
@ -47,7 +48,7 @@ extern ANativeWindow* Android_JNI_GetNativeWindow(void);
|
|||||||
extern int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi);
|
extern int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi);
|
||||||
|
|
||||||
/* Audio support */
|
/* Audio support */
|
||||||
extern int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames);
|
extern int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec);
|
||||||
extern void* Android_JNI_GetAudioBuffer(void);
|
extern void* Android_JNI_GetAudioBuffer(void);
|
||||||
extern void Android_JNI_WriteAudioBuffer(void);
|
extern void Android_JNI_WriteAudioBuffer(void);
|
||||||
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
|
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
|
||||||
|
Loading…
Reference in New Issue
Block a user