android: Reworked audio backends for SDL3 audio API.
This involved moving an `#ifdef` out of SDL_audio.c for thread priority, so the default ThreadInit now does the usual stuff for non-Android platforms, the Android platforms provide an implementatin of ThreadInit with their side of the `#ifdef` and other platforms that implement ThreadInit incorporated the appropriate code...which is why WASAPI is touched in here. The Android bits compile, but have not been tested, and there was some reworkings in the Java bits, so this might need some further fixes still.main
parent
54af687210
commit
5ff87c6d4a
|
@ -21,8 +21,6 @@ public class SDLAudioManager {
|
|||
protected static AudioRecord mAudioRecord;
|
||||
protected static Context mContext;
|
||||
|
||||
private static final int[] NO_DEVICES = {};
|
||||
|
||||
private static AudioDeviceCallback mAudioDeviceCallback;
|
||||
|
||||
public static void initialize() {
|
||||
|
@ -36,7 +34,7 @@ public class SDLAudioManager {
|
|||
@Override
|
||||
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||
for (AudioDeviceInfo deviceInfo : addedDevices) {
|
||||
addAudioDevice(deviceInfo.isSink(), deviceInfo.getId());
|
||||
addAudioDevice(deviceInfo.isSink(), deviceInfo.getProductName().toString(), deviceInfo.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,13 +50,10 @@ public class SDLAudioManager {
|
|||
|
||||
public static void setContext(Context context) {
|
||||
mContext = context;
|
||||
if (context != null) {
|
||||
registerAudioDeviceCallback();
|
||||
}
|
||||
}
|
||||
|
||||
public static void release(Context context) {
|
||||
unregisterAudioDeviceCallback(context);
|
||||
// no-op atm
|
||||
}
|
||||
|
||||
// Audio
|
||||
|
@ -311,65 +306,20 @@ public class SDLAudioManager {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static void registerAudioDeviceCallback() {
|
||||
public static void registerAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void unregisterAudioDeviceCallback(Context context) {
|
||||
public static void unregisterAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] ArrayListToArray(ArrayList<Integer> integers)
|
||||
{
|
||||
int[] ret = new int[integers.size()];
|
||||
for (int i=0; i < ret.length; i++) {
|
||||
ret[i] = integers.get(i).intValue();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] getAudioOutputDevices() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
ArrayList<Integer> arrlist = new ArrayList<Integer>();
|
||||
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
|
||||
/* Device cannot be opened */
|
||||
if (dev.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
|
||||
continue;
|
||||
}
|
||||
arrlist.add(dev.getId());
|
||||
}
|
||||
return ArrayListToArray(arrlist);
|
||||
} else {
|
||||
return NO_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] getAudioInputDevices() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
ArrayList<Integer> arrlist = new ArrayList<Integer>();
|
||||
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
|
||||
arrlist.add(dev.getId());
|
||||
}
|
||||
return ArrayListToArray(arrlist);
|
||||
} else {
|
||||
return NO_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
|
@ -535,6 +485,6 @@ public class SDLAudioManager {
|
|||
|
||||
public static native void removeAudioDevice(boolean isCapture, int deviceId);
|
||||
|
||||
public static native void addAudioDevice(boolean isCapture, int deviceId);
|
||||
public static native void addAudioDevice(boolean isCapture, String name, int deviceId);
|
||||
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#include "../thread/SDL_systhread.h"
|
||||
#include "../SDL_utils_c.h"
|
||||
|
||||
extern void Android_JNI_AudioSetThreadPriority(int, int); // we need this on Android in the audio device threads.
|
||||
|
||||
// Available audio drivers
|
||||
static const AudioBootStrap *const bootstrap[] = {
|
||||
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO
|
||||
|
@ -412,7 +410,6 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
|
|||
|
||||
// stubs for audio drivers that don't need a specific entry point...
|
||||
|
||||
static void SDL_AudioThreadInit_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
static void SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { /* no-op. */ }
|
||||
|
@ -422,6 +419,11 @@ static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */
|
|||
static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ }
|
||||
static void SDL_AudioFreeDeviceHandle_Default(SDL_AudioDevice *device) { /* no-op. */ }
|
||||
|
||||
static void SDL_AudioThreadInit_Default(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
|
||||
static void SDL_AudioDetectDevices_Default(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
// you have to write your own implementation if these assertions fail.
|
||||
|
@ -679,15 +681,6 @@ void SDL_AudioThreadFinalize(SDL_AudioDevice *device)
|
|||
void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_assert(!device->iscapture);
|
||||
|
||||
// The audio mixing is always a high priority thread
|
||||
#ifdef SDL_AUDIO_DRIVER_ANDROID
|
||||
Android_JNI_AudioSetThreadPriority(SDL_FALSE, device->id);
|
||||
#else
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
||||
#endif
|
||||
|
||||
// Perform any thread setup
|
||||
current_audio.impl.ThreadInit(device);
|
||||
}
|
||||
|
||||
|
@ -781,14 +774,6 @@ static int SDLCALL OutputAudioThread(void *devicep) // thread entry point
|
|||
void SDL_CaptureAudioThreadSetup(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_assert(device->iscapture);
|
||||
|
||||
// Audio capture is always a high priority thread (!!! FIXME: _should_ it be?)
|
||||
#ifdef SDL_AUDIO_DRIVER_ANDROID
|
||||
Android_JNI_AudioSetThreadPriority(SDL_TRUE, device->id);
|
||||
#else
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
||||
#endif
|
||||
|
||||
current_audio.impl.ThreadInit(device);
|
||||
}
|
||||
|
||||
|
@ -1068,7 +1053,6 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
|
|||
|
||||
if ((devid == 0) && is_default) {
|
||||
return SDL_SetError("No default audio device available");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid);
|
||||
|
|
|
@ -34,16 +34,13 @@ struct SDL_PrivateAudioData
|
|||
{
|
||||
AAudioStream *stream;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
Uint8 *mixbuf; // Raw mixing buffer
|
||||
int frame_size;
|
||||
|
||||
/* Resume device if it was paused automatically */
|
||||
int resume;
|
||||
int resume; // Resume device if it was paused automatically
|
||||
};
|
||||
|
||||
/* Debug */
|
||||
// Debug
|
||||
#if 0
|
||||
#define LOGI(...) SDL_Log(__VA_ARGS__);
|
||||
#else
|
||||
|
@ -52,7 +49,6 @@ struct SDL_PrivateAudioData
|
|||
|
||||
typedef struct AAUDIO_Data
|
||||
{
|
||||
AAudioStreamBuilder *builder;
|
||||
void *handle;
|
||||
#define SDL_PROC(ret, func, params) ret (*func) params;
|
||||
#include "SDL_aaudiofuncs.h"
|
||||
|
@ -79,11 +75,14 @@ static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_re
|
|||
|
||||
#define LIB_AAUDIO_SO "libaaudio.so"
|
||||
|
||||
static int AAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
aaudio_result_t res;
|
||||
|
||||
SDL_assert(device->handle != NULL); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero.
|
||||
|
||||
LOGI(__func__);
|
||||
|
||||
if (iscapture) {
|
||||
|
@ -93,75 +92,91 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
|||
}
|
||||
}
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
hidden = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
private = _this->hidden;
|
||||
|
||||
ctx.AAudioStreamBuilder_setSampleRate(ctx.builder, _this->spec.freq);
|
||||
ctx.AAudioStreamBuilder_setChannelCount(ctx.builder, _this->spec.channels);
|
||||
if(devname != NULL) {
|
||||
int aaudio_device_id = SDL_atoi(devname);
|
||||
AAudioStreamBuilder *builder = NULL;
|
||||
res = ctx.AAudio_createStreamBuilder(&builder);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
|
||||
return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res);
|
||||
} else if (builder == NULL) {
|
||||
LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
|
||||
return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL");
|
||||
}
|
||||
|
||||
// !!! FIXME: call AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); ?
|
||||
|
||||
ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq);
|
||||
ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels);
|
||||
|
||||
const int aaudio_device_id = (int) ((size_t) device->handle);
|
||||
LOGI("Opening device id %d", aaudio_device_id);
|
||||
ctx.AAudioStreamBuilder_setDeviceId(ctx.builder, aaudio_device_id);
|
||||
}
|
||||
{
|
||||
aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
|
||||
ctx.AAudioStreamBuilder_setDirection(ctx.builder, direction);
|
||||
}
|
||||
{
|
||||
aaudio_format_t format = AAUDIO_FORMAT_PCM_FLOAT;
|
||||
if (_this->spec.format == SDL_AUDIO_S16SYS) {
|
||||
format = AAUDIO_FORMAT_PCM_I16;
|
||||
} else if (_this->spec.format == SDL_AUDIO_S16SYS) {
|
||||
ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id);
|
||||
|
||||
const aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
|
||||
ctx.AAudioStreamBuilder_setDirection(builder, direction);
|
||||
aaudio_format_t format;
|
||||
if (device->spec.format == SDL_AUDIO_S32SYS) {
|
||||
format = AAUDIO_FORMAT_PCM_I32;
|
||||
} else if (device->spec.format == SDL_AUDIO_F32SYS) {
|
||||
format = AAUDIO_FORMAT_PCM_FLOAT;
|
||||
}
|
||||
ctx.AAudioStreamBuilder_setFormat(ctx.builder, format);
|
||||
} else {
|
||||
format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else.
|
||||
}
|
||||
|
||||
ctx.AAudioStreamBuilder_setErrorCallback(ctx.builder, AAUDIO_errorCallback, private);
|
||||
ctx.AAudioStreamBuilder_setFormat(builder, format);
|
||||
|
||||
ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, hidden);
|
||||
|
||||
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
|
||||
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
|
||||
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
|
||||
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
|
||||
device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
|
||||
|
||||
res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream);
|
||||
ctx.AAudioStreamBuilder_delete(builder);
|
||||
|
||||
res = ctx.AAudioStreamBuilder_openStream(ctx.builder, &private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
|
||||
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
|
||||
_this->spec.freq = ctx.AAudioStream_getSampleRate(private->stream);
|
||||
_this->spec.channels = ctx.AAudioStream_getChannelCount(private->stream);
|
||||
{
|
||||
aaudio_format_t fmt = ctx.AAudioStream_getFormat(private->stream);
|
||||
if (fmt == AAUDIO_FORMAT_PCM_I16) {
|
||||
_this->spec.format = SDL_AUDIO_S16SYS;
|
||||
} else if (fmt == AAUDIO_FORMAT_PCM_FLOAT) {
|
||||
_this->spec.format = SDL_AUDIO_F32SYS;
|
||||
}
|
||||
device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream);
|
||||
device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream);
|
||||
|
||||
format = ctx.AAudioStream_getFormat(hidden->stream);
|
||||
if (format == AAUDIO_FORMAT_PCM_I16) {
|
||||
device->spec.format = SDL_AUDIO_S16SYS;
|
||||
} else if (format == AAUDIO_FORMAT_PCM_I32) {
|
||||
device->spec.format = SDL_AUDIO_S32SYS;
|
||||
} else if (format == AAUDIO_FORMAT_PCM_FLOAT) {
|
||||
device->spec.format = SDL_AUDIO_F32SYS;
|
||||
} else {
|
||||
return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format);
|
||||
}
|
||||
|
||||
device->sample_frames = ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 2;
|
||||
|
||||
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
|
||||
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
|
||||
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
|
||||
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
|
||||
device->spec.channels, SDL_AUDIO_ISBIGENDIAN(device->spec.format) ? "BE" : "LE", device->sample_frames);
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
// Allocate mixing buffer
|
||||
if (!iscapture) {
|
||||
private->mixlen = _this->spec.size;
|
||||
private->mixbuf = (Uint8 *)SDL_malloc(private->mixlen);
|
||||
if (private->mixbuf == NULL) {
|
||||
hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
|
||||
if (hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(private->mixbuf, _this->spec.silence, _this->spec.size);
|
||||
SDL_memset(hidden->mixbuf, device->silence_value, device->buffer_size);
|
||||
}
|
||||
|
||||
private->frame_size = _this->spec.channels * (SDL_AUDIO_BITSIZE(_this->spec.format) / 8);
|
||||
hidden->frame_size = device->spec.channels * (SDL_AUDIO_BITSIZE(device->spec.format) / 8);
|
||||
|
||||
res = ctx.AAudioStream_requestStart(private->stream);
|
||||
res = ctx.AAudioStream_requestStart(hidden->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture);
|
||||
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
|
@ -171,55 +186,51 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void AAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
static void AAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = _this->hidden;
|
||||
aaudio_result_t res;
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
if (hidden) {
|
||||
LOGI(__func__);
|
||||
|
||||
if (private->stream) {
|
||||
res = ctx.AAudioStream_requestStop(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStop %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
return;
|
||||
if (hidden->stream) {
|
||||
ctx.AAudioStream_requestStop(hidden->stream);
|
||||
ctx.AAudioStream_close(hidden->stream);
|
||||
}
|
||||
|
||||
res = ctx.AAudioStream_close(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStreamBuilder_delete %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
return;
|
||||
SDL_free(hidden->mixbuf);
|
||||
SDL_free(hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = _this->hidden;
|
||||
return private->mixbuf;
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void AAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = _this->hidden;
|
||||
aaudio_result_t res;
|
||||
int64_t timeoutNanoseconds = 1 * 1000 * 1000; /* 8 ms */
|
||||
res = ctx.AAudioStream_write(private->stream, private->mixbuf, private->mixlen / private->frame_size, timeoutNanoseconds);
|
||||
AAudioStream *stream = device->hidden->stream;
|
||||
while (!SDL_AtomicGet(&device->shutdown) && ((int) ctx.AAudioStream_getBufferSizeInFrames(stream)) < device->sample_frames) {
|
||||
SDL_Delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
AAudioStream *stream = device->hidden->stream;
|
||||
const aaudio_result_t res = ctx.AAudioStream_write(stream, buffer, device->sample_frames, 0);
|
||||
if (res < 0) {
|
||||
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
} else {
|
||||
LOGI("SDL AAudio play: %d frames, wanted:%d frames", (int)res, private->mixlen / private->frame_size);
|
||||
LOGI("SDL AAudio play: %d frames, wanted:%d frames", (int)res, sample_frames);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Log under-run count */
|
||||
// Log under-run count
|
||||
{
|
||||
static int prev = 0;
|
||||
int32_t cnt = ctx.AAudioStream_getXRunCount(private->stream);
|
||||
int32_t cnt = ctx.AAudioStream_getXRunCount(hidden->stream);
|
||||
if (cnt != prev) {
|
||||
SDL_Log("AAudio underrun: %d - total: %d", cnt - prev, cnt);
|
||||
prev = cnt;
|
||||
|
@ -228,41 +239,31 @@ static void AAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
|||
#endif
|
||||
}
|
||||
|
||||
static int AAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
static int AAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = _this->hidden;
|
||||
aaudio_result_t res;
|
||||
int64_t timeoutNanoseconds = 8 * 1000 * 1000; /* 8 ms */
|
||||
res = ctx.AAudioStream_read(private->stream, buffer, buflen / private->frame_size, timeoutNanoseconds);
|
||||
const aaudio_result_t res = ctx.AAudioStream_read(device->hidden->stream, buffer, device->sample_frames, 0);
|
||||
if (res < 0) {
|
||||
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
return -1;
|
||||
}
|
||||
LOGI("SDL AAudio capture:%d frames, wanted:%d frames", (int)res, buflen / private->frame_size);
|
||||
return res * private->frame_size;
|
||||
LOGI("SDL AAudio capture:%d frames, wanted:%d frames", (int)res, buflen / device->hidden->frame_size);
|
||||
return res * device->hidden->frame_size;
|
||||
}
|
||||
|
||||
static void AAUDIO_Deinitialize(void)
|
||||
{
|
||||
Android_StopAudioHotplug();
|
||||
|
||||
LOGI(__func__);
|
||||
if (ctx.handle) {
|
||||
if (ctx.builder) {
|
||||
aaudio_result_t res;
|
||||
res = ctx.AAudioStreamBuilder_delete(ctx.builder);
|
||||
if (res != AAUDIO_OK) {
|
||||
SDL_SetError("Failed AAudioStreamBuilder_delete %s", ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
SDL_UnloadObject(ctx.handle);
|
||||
}
|
||||
ctx.handle = NULL;
|
||||
ctx.builder = NULL;
|
||||
SDL_zero(ctx);
|
||||
LOGI("End AAUDIO %s", SDL_GetError());
|
||||
}
|
||||
|
||||
static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
aaudio_result_t res;
|
||||
LOGI(__func__);
|
||||
|
||||
/* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release,
|
||||
|
@ -279,189 +280,96 @@ static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
|
|||
ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO);
|
||||
if (ctx.handle == NULL) {
|
||||
LOGI("SDL couldn't find " LIB_AAUDIO_SO);
|
||||
goto failure;
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (AAUDIO_LoadFunctions(&ctx) < 0) {
|
||||
goto failure;
|
||||
SDL_UnloadObject(ctx.handle);
|
||||
SDL_zero(ctx);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
res = ctx.AAudio_createStreamBuilder(&ctx.builder);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (ctx.builder == NULL) {
|
||||
LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
impl->DetectDevices = Android_DetectDevices;
|
||||
impl->ThreadInit = Android_AudioThreadInit;
|
||||
impl->DetectDevices = Android_StartAudioHotplug;
|
||||
impl->Deinitialize = AAUDIO_Deinitialize;
|
||||
impl->OpenDevice = AAUDIO_OpenDevice;
|
||||
impl->CloseDevice = AAUDIO_CloseDevice;
|
||||
impl->WaitDevice = AAUDIO_WaitDevice;
|
||||
impl->PlayDevice = AAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = AAUDIO_GetDeviceBuf;
|
||||
impl->WaitCaptureDevice = AAUDIO_WaitDevice;
|
||||
impl->CaptureFromDevice = AAUDIO_CaptureFromDevice;
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
|
||||
/* and the capabilities */
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
|
||||
|
||||
/* this audio target is available. */
|
||||
LOGI("SDL AAUDIO_Init OK");
|
||||
return SDL_TRUE;
|
||||
|
||||
failure:
|
||||
if (ctx.handle) {
|
||||
if (ctx.builder) {
|
||||
ctx.AAudioStreamBuilder_delete(ctx.builder);
|
||||
}
|
||||
SDL_UnloadObject(ctx.handle);
|
||||
}
|
||||
ctx.handle = NULL;
|
||||
ctx.builder = NULL;
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
AudioBootStrap AAUDIO_bootstrap = {
|
||||
"AAudio", "AAudio audio driver", AAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
/* Pause (block) all non already paused audio devices by taking their mixer lock */
|
||||
void AAUDIO_PauseDevices(void)
|
||||
|
||||
static SDL_bool PauseOneDevice(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
int i;
|
||||
struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden;
|
||||
if (hidden != NULL) {
|
||||
if (hidden->stream) {
|
||||
aaudio_result_t res;
|
||||
|
||||
/* AAUDIO driver is not used */
|
||||
if (ctx.handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < get_max_num_audio_dev(); i++) {
|
||||
SDL_AudioDevice *_this = get_audio_dev(i);
|
||||
SDL_AudioDevice *audioDevice = NULL;
|
||||
SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
if (_this == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
captureDevice = _this;
|
||||
if (device->iscapture) {
|
||||
// Pause() isn't implemented for 'capture', use Stop()
|
||||
res = ctx.AAudioStream_requestStop(hidden->stream);
|
||||
} else {
|
||||
audioDevice = _this;
|
||||
res = ctx.AAudioStream_requestPause(hidden->stream);
|
||||
}
|
||||
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
|
||||
if (private->stream) {
|
||||
aaudio_result_t res = ctx.AAudioStream_requestPause(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestPause %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
|
||||
SDL_LockMutex(device->lock);
|
||||
hidden->resume = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE; // keep enumerating.
|
||||
}
|
||||
|
||||
if (SDL_AtomicGet(&audioDevice->paused)) {
|
||||
/* The device is already paused, leave it alone */
|
||||
private->resume = SDL_FALSE;
|
||||
} else {
|
||||
SDL_LockMutex(audioDevice->mixer_lock);
|
||||
SDL_AtomicSet(&audioDevice->paused, 1);
|
||||
private->resume = SDL_TRUE;
|
||||
// Pause (block) all non already paused audio devices by taking their mixer lock
|
||||
void AAUDIO_PauseDevices(void)
|
||||
{
|
||||
if (ctx.handle != NULL) { // AAUDIO driver is used?
|
||||
(void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
// Resume (unblock) all non already paused audio devices by releasing their mixer lock
|
||||
static SDL_bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
if (hidden != NULL) {
|
||||
if (hidden->resume) {
|
||||
hidden->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(device->lock);
|
||||
}
|
||||
|
||||
if (private->stream) {
|
||||
/* Pause() isn't implemented for 'capture', use Stop() */
|
||||
aaudio_result_t res = ctx.AAudioStream_requestStop(private->stream);
|
||||
if (hidden->stream) {
|
||||
aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStop %d", res);
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_AtomicGet(&captureDevice->paused)) {
|
||||
/* The device is already paused, leave it alone */
|
||||
private->resume = SDL_FALSE;
|
||||
} else {
|
||||
SDL_LockMutex(captureDevice->mixer_lock);
|
||||
SDL_AtomicSet(&captureDevice->paused, 1);
|
||||
private->resume = SDL_TRUE;
|
||||
}
|
||||
return SDL_FALSE; // keep enumerating.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Resume (unblock) all non already paused audio devices by releasing their mixer lock */
|
||||
void AAUDIO_ResumeDevices(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* AAUDIO driver is not used */
|
||||
if (ctx.handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < get_max_num_audio_dev(); i++) {
|
||||
SDL_AudioDevice *_this = get_audio_dev(i);
|
||||
SDL_AudioDevice *audioDevice = NULL;
|
||||
SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
if (_this == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
captureDevice = _this;
|
||||
} else {
|
||||
audioDevice = _this;
|
||||
}
|
||||
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = audioDevice->hidden;
|
||||
|
||||
if (private->resume) {
|
||||
SDL_AtomicSet(&audioDevice->paused, 0);
|
||||
private->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(audioDevice->mixer_lock);
|
||||
}
|
||||
|
||||
if (private->stream) {
|
||||
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = audioDevice->hidden;
|
||||
|
||||
if (private->resume) {
|
||||
SDL_AtomicSet(&captureDevice->paused, 0);
|
||||
private->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(captureDevice->mixer_lock);
|
||||
}
|
||||
|
||||
if (private->stream) {
|
||||
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx.handle != NULL) { // AAUDIO driver is used?
|
||||
(void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -470,50 +378,29 @@ void AAUDIO_ResumeDevices(void)
|
|||
None of the standard state queries indicate any problem in my testing. And the error callback doesn't actually get called.
|
||||
But, AAudioStream_getTimestamp() does return AAUDIO_ERROR_INVALID_STATE
|
||||
*/
|
||||
SDL_bool AAUDIO_DetectBrokenPlayState(void)
|
||||
static SDL_bool DetectBrokenPlayStatePerDevice(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* AAUDIO driver is not used */
|
||||
if (ctx.handle == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
for (i = 0; i < get_max_num_audio_dev(); i++) {
|
||||
SDL_AudioDevice *_this = get_audio_dev(i);
|
||||
SDL_AudioDevice *audioDevice = NULL;
|
||||
SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
if (_this == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
captureDevice = _this;
|
||||
} else {
|
||||
audioDevice = _this;
|
||||
}
|
||||
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = audioDevice->hidden;
|
||||
SDL_assert(device != NULL);
|
||||
if (!device->iscapture && device->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
int64_t framePosition, timeNanoseconds;
|
||||
|
||||
aaudio_result_t res = ctx.AAudioStream_getTimestamp(private->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds);
|
||||
aaudio_result_t res = ctx.AAudioStream_getTimestamp(hidden->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds);
|
||||
if (res == AAUDIO_ERROR_INVALID_STATE) {
|
||||
aaudio_stream_state_t currentState = ctx.AAudioStream_getState(private->stream);
|
||||
/* AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing. */
|
||||
aaudio_stream_state_t currentState = ctx.AAudioStream_getState(hidden->stream);
|
||||
// AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing.
|
||||
if (currentState == AAUDIO_STREAM_STATE_STARTED) {
|
||||
LOGI("SDL AAUDIO_DetectBrokenPlayState: detected invalid audio device state: AAudioStream_getTimestamp result=%d, framePosition=%lld, timeNanoseconds=%lld, getState=%d", (int)res, (long long)framePosition, (long long)timeNanoseconds, (int)currentState);
|
||||
return SDL_TRUE;
|
||||
return SDL_TRUE; // this guy.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_FALSE; // enumerate more devices.
|
||||
}
|
||||
|
||||
(void) captureDevice;
|
||||
SDL_bool AAUDIO_DetectBrokenPlayState(void)
|
||||
{
|
||||
return (ctx.handle && SDL_FindPhysicalAudioDeviceByCallback(DetectBrokenPlayStatePerDevice, NULL) != NULL) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_AAUDIO */
|
||||
#endif // SDL_AUDIO_DRIVER_AAUDIO
|
||||
|
|
|
@ -55,9 +55,9 @@ SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_waitForStateChange, (AAudioStream
|
|||
SDL_PROC(aaudio_result_t, AAudioStream_read, (AAudioStream * stream, void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_write, (AAudioStream * stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_setBufferSizeInFrames, (AAudioStream * stream, int32_t numFrames))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerBurst, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream))
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#ifdef SDL_AUDIO_DRIVER_ANDROID
|
||||
|
||||
/* Output audio to Android */
|
||||
// Output audio to Android (legacy interface)
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
|
@ -34,185 +34,157 @@
|
|||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* Resume device if it was paused automatically */
|
||||
int resume;
|
||||
int resume; // Resume device if it was paused automatically
|
||||
};
|
||||
|
||||
static SDL_AudioDevice *audioDevice = NULL;
|
||||
static SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
|
||||
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
|
||||
if (iscapture) {
|
||||
if (captureDevice) {
|
||||
return SDL_SetError("An audio capture device is already opened");
|
||||
}
|
||||
}
|
||||
|
||||
if (!iscapture) {
|
||||
captureDevice = device;
|
||||
} else {
|
||||
if (audioDevice) {
|
||||
return SDL_SetError("An audio playback device is already opened");
|
||||
}
|
||||
audioDevice = device;
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
captureDevice = _this;
|
||||
} else {
|
||||
audioDevice = _this;
|
||||
}
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if ((test_format == SDL_AUDIO_U8) ||
|
||||
(test_format == SDL_AUDIO_S16) ||
|
||||
(test_format == SDL_AUDIO_F32)) {
|
||||
_this->spec.format = test_format;
|
||||
device->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
/* Didn't find a compatible format :( */
|
||||
return SDL_SetError("%s: Unsupported audio format", "android");
|
||||
return SDL_SetError("android: Unsupported audio format");
|
||||
}
|
||||
|
||||
{
|
||||
int audio_device_id = 0;
|
||||
if (devname != NULL) {
|
||||
audio_device_id = SDL_atoi(devname);
|
||||
}
|
||||
if (Android_JNI_OpenAudioDevice(iscapture, audio_device_id, &_this->spec) < 0) {
|
||||
if (Android_JNI_OpenAudioDevice(device) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
// !!! FIXME: this needs a WaitDevice implementation.
|
||||
|
||||
static void ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
Android_JNI_WriteAudioBuffer();
|
||||
}
|
||||
|
||||
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return Android_JNI_GetAudioBuffer();
|
||||
}
|
||||
|
||||
static int ANDROIDAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
static int ANDROIDAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
return Android_JNI_CaptureAudioBuffer(buffer, buflen);
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *_this)
|
||||
static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
Android_JNI_FlushCapturedAudio();
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
static void ANDROIDAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
/* At this point SDL_CloseAudioDevice via close_audio_device took care of terminating the audio thread
|
||||
so it's safe to terminate the Java side buffer and AudioTrack
|
||||
*/
|
||||
Android_JNI_CloseAudioDevice(_this->iscapture);
|
||||
if (_this->iscapture) {
|
||||
SDL_assert(captureDevice == _this);
|
||||
if (device->hidden) {
|
||||
Android_JNI_CloseAudioDevice(device->iscapture);
|
||||
if (device->iscapture) {
|
||||
SDL_assert(captureDevice == device);
|
||||
captureDevice = NULL;
|
||||
} else {
|
||||
SDL_assert(audioDevice == _this);
|
||||
SDL_assert(audioDevice == device);
|
||||
audioDevice = NULL;
|
||||
}
|
||||
SDL_free(_this->hidden);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = Android_DetectDevices;
|
||||
impl->ThreadInit = Android_AudioThreadInit;
|
||||
impl->DetectDevices = Android_StartAudioHotplug;
|
||||
impl->Deinitialize = Android_StopAudioHotplug;
|
||||
impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
|
||||
impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
|
||||
impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
|
||||
/* and the capabilities */
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap ANDROIDAUDIO_bootstrap = {
|
||||
"android", "SDL Android audio driver", ANDROIDAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
/* Pause (block) all non already paused audio devices by taking their mixer lock */
|
||||
// Pause (block) all non already paused audio devices by taking their mixer lock
|
||||
void ANDROIDAUDIO_PauseDevices(void)
|
||||
{
|
||||
/* TODO: Handle multiple devices? */
|
||||
struct SDL_PrivateAudioData *private;
|
||||
// TODO: Handle multiple devices?
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
if (SDL_AtomicGet(&audioDevice->paused)) {
|
||||
/* The device is already paused, leave it alone */
|
||||
private->resume = SDL_FALSE;
|
||||
} else {
|
||||
SDL_LockMutex(audioDevice->mixer_lock);
|
||||
SDL_AtomicSet(&audioDevice->paused, 1);
|
||||
private->resume = SDL_TRUE;
|
||||
}
|
||||
hidden = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
SDL_LockMutex(audioDevice->lock);
|
||||
hidden->resume = SDL_TRUE;
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
private = (struct SDL_PrivateAudioData *)captureDevice->hidden;
|
||||
if (SDL_AtomicGet(&captureDevice->paused)) {
|
||||
/* The device is already paused, leave it alone */
|
||||
private->resume = SDL_FALSE;
|
||||
} else {
|
||||
SDL_LockMutex(captureDevice->mixer_lock);
|
||||
SDL_AtomicSet(&captureDevice->paused, 1);
|
||||
private->resume = SDL_TRUE;
|
||||
}
|
||||
hidden = (struct SDL_PrivateAudioData *)captureDevice->hidden;
|
||||
SDL_LockMutex(captureDevice->lock);
|
||||
hidden->resume = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resume (unblock) all non already paused audio devices by releasing their mixer lock */
|
||||
// Resume (unblock) all non already paused audio devices by releasing their mixer lock
|
||||
void ANDROIDAUDIO_ResumeDevices(void)
|
||||
{
|
||||
/* TODO: Handle multiple devices? */
|
||||
struct SDL_PrivateAudioData *private;
|
||||
// TODO: Handle multiple devices?
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
if (private->resume) {
|
||||
SDL_AtomicSet(&audioDevice->paused, 0);
|
||||
private->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(audioDevice->mixer_lock);
|
||||
hidden = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
if (hidden->resume) {
|
||||
hidden->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(audioDevice->lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
private = (struct SDL_PrivateAudioData *)captureDevice->hidden;
|
||||
if (private->resume) {
|
||||
SDL_AtomicSet(&captureDevice->paused, 0);
|
||||
private->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(captureDevice->mixer_lock);
|
||||
hidden = (struct SDL_PrivateAudioData *)captureDevice->hidden;
|
||||
if (hidden->resume) {
|
||||
hidden->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(captureDevice->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ANDROID */
|
||||
#endif // SDL_AUDIO_DRIVER_ANDROID
|
||||
|
|
|
@ -22,9 +22,8 @@
|
|||
|
||||
#ifdef SDL_AUDIO_DRIVER_OPENSLES
|
||||
|
||||
/* For more discussion of low latency audio on Android, see this:
|
||||
https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
|
||||
*/
|
||||
// For more discussion of low latency audio on Android, see this:
|
||||
// https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
|
@ -36,7 +35,7 @@
|
|||
#include <android/log.h>
|
||||
|
||||
|
||||
#define NUM_BUFFERS 2 /* -- Don't lower this! */
|
||||
#define NUM_BUFFERS 2 // -- Don't lower this!
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
|
@ -83,14 +82,14 @@ struct SDL_PrivateAudioData
|
|||
#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY)
|
||||
#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
|
||||
|
||||
/* engine interfaces */
|
||||
// engine interfaces
|
||||
static SLObjectItf engineObject = NULL;
|
||||
static SLEngineItf engineEngine = NULL;
|
||||
|
||||
/* output mix interfaces */
|
||||
// output mix interfaces
|
||||
static SLObjectItf outputMixObject = NULL;
|
||||
|
||||
/* buffer queue player interfaces */
|
||||
// buffer queue player interfaces
|
||||
static SLObjectItf bqPlayerObject = NULL;
|
||||
static SLPlayItf bqPlayerPlay = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
|
||||
|
@ -98,7 +97,7 @@ static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
|
|||
static SLVolumeItf bqPlayerVolume;
|
||||
#endif
|
||||
|
||||
/* recorder interfaces */
|
||||
// recorder interfaces
|
||||
static SLObjectItf recorderObject = NULL;
|
||||
static SLRecordItf recorderRecord = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
|
||||
|
@ -123,13 +122,13 @@ static void openslES_DestroyEngine(void)
|
|||
{
|
||||
LOGI("openslES_DestroyEngine()");
|
||||
|
||||
/* destroy output mix object, and invalidate all associated interfaces */
|
||||
// destroy output mix object, and invalidate all associated interfaces
|
||||
if (outputMixObject != NULL) {
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = NULL;
|
||||
}
|
||||
|
||||
/* destroy engine object, and invalidate all associated interfaces */
|
||||
// destroy engine object, and invalidate all associated interfaces
|
||||
if (engineObject != NULL) {
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = NULL;
|
||||
|
@ -145,7 +144,7 @@ static int openslES_CreateEngine(void)
|
|||
|
||||
LOGI("openSLES_CreateEngine()");
|
||||
|
||||
/* create engine */
|
||||
// create engine
|
||||
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("slCreateEngine failed: %d", result);
|
||||
|
@ -153,7 +152,7 @@ static int openslES_CreateEngine(void)
|
|||
}
|
||||
LOGI("slCreateEngine OK");
|
||||
|
||||
/* realize the engine */
|
||||
// realize the engine
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeEngine failed: %d", result);
|
||||
|
@ -161,7 +160,7 @@ static int openslES_CreateEngine(void)
|
|||
}
|
||||
LOGI("RealizeEngine OK");
|
||||
|
||||
/* get the engine interface, which is needed in order to create other objects */
|
||||
// get the engine interface, which is needed in order to create other objects
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("EngineGetInterface failed: %d", result);
|
||||
|
@ -169,7 +168,7 @@ static int openslES_CreateEngine(void)
|
|||
}
|
||||
LOGI("EngineGetInterface OK");
|
||||
|
||||
/* create output mix */
|
||||
// create output mix
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateOutputMix failed: %d", result);
|
||||
|
@ -177,7 +176,7 @@ static int openslES_CreateEngine(void)
|
|||
}
|
||||
LOGI("CreateOutputMix OK");
|
||||
|
||||
/* realize the output mix */
|
||||
// realize the output mix
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeOutputMix failed: %d", result);
|
||||
|
@ -190,7 +189,7 @@ error:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* this callback handler is called every time a buffer finishes recording */
|
||||
// this callback handler is called every time a buffer finishes recording
|
||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
|
||||
|
@ -199,12 +198,12 @@ static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
|||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_DestroyPCMRecorder(SDL_AudioDevice *_this)
|
||||
static void openslES_DestroyPCMRecorder(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
SLresult result;
|
||||
|
||||
/* stop recording */
|
||||
// stop recording
|
||||
if (recorderRecord != NULL) {
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
|
@ -212,7 +211,7 @@ static void openslES_DestroyPCMRecorder(SDL_AudioDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
/* destroy audio recorder object, and invalidate all associated interfaces */
|
||||
// destroy audio recorder object, and invalidate all associated interfaces
|
||||
if (recorderObject != NULL) {
|
||||
(*recorderObject)->Destroy(recorderObject);
|
||||
recorderObject = NULL;
|
||||
|
@ -230,9 +229,9 @@ static void openslES_DestroyPCMRecorder(SDL_AudioDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
|
||||
static int openslES_CreatePCMRecorder(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
SLDataFormat_PCM format_pcm;
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
SLDataSink audioSnk;
|
||||
|
@ -248,19 +247,19 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
|
|||
return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
|
||||
}
|
||||
|
||||
/* Just go with signed 16-bit audio as it's the most compatible */
|
||||
_this->spec.format = SDL_AUDIO_S16SYS;
|
||||
_this->spec.channels = 1;
|
||||
/*_this->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
|
||||
// Just go with signed 16-bit audio as it's the most compatible
|
||||
device->spec.format = SDL_AUDIO_S16SYS;
|
||||
device->spec.channels = 1;
|
||||
//device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
// Update the fragment size as size in bytes
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
LOGI("Try to open %u hz %u bit chan %u %s samples %u",
|
||||
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
|
||||
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
|
||||
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
|
||||
device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
|
||||
|
||||
/* configure audio source */
|
||||
// configure audio source
|
||||
loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
|
||||
loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
|
||||
loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
|
||||
|
@ -268,93 +267,93 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
|
|||
audioSrc.pLocator = &loc_dev;
|
||||
audioSrc.pFormat = NULL;
|
||||
|
||||
/* configure audio sink */
|
||||
// configure audio sink
|
||||
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
loc_bufq.numBuffers = NUM_BUFFERS;
|
||||
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = _this->spec.channels;
|
||||
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */
|
||||
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
format_pcm.numChannels = device->spec.channels;
|
||||
format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
|
||||
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
|
||||
|
||||
audioSnk.pLocator = &loc_bufq;
|
||||
audioSnk.pFormat = &format_pcm;
|
||||
|
||||
/* create audio recorder */
|
||||
/* (requires the RECORD_AUDIO permission) */
|
||||
// create audio recorder
|
||||
// (requires the RECORD_AUDIO permission)
|
||||
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateAudioRecorder failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* realize the recorder */
|
||||
// realize the recorder
|
||||
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get the record interface */
|
||||
// get the record interface
|
||||
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_RECORD interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get the buffer queue interface */
|
||||
// get the buffer queue interface
|
||||
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* register callback on the buffer queue */
|
||||
/* context is '(SDL_PrivateAudioData *)_this->hidden' */
|
||||
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, _this->hidden);
|
||||
// register callback on the buffer queue
|
||||
// context is '(SDL_PrivateAudioData *)device->hidden'
|
||||
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, device->hidden);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RegisterCallback failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Create the audio buffer semaphore */
|
||||
// Create the audio buffer semaphore
|
||||
audiodata->playsem = SDL_CreateSemaphore(0);
|
||||
if (!audiodata->playsem) {
|
||||
LOGE("cannot create Semaphore!");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Create the sound buffers */
|
||||
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size);
|
||||
// Create the sound buffers
|
||||
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
|
||||
if (audiodata->mixbuff == NULL) {
|
||||
LOGE("mixbuffer allocate - out of memory");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
audiodata->pmixbuff[i] = audiodata->mixbuff + i * _this->spec.size;
|
||||
audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size;
|
||||
}
|
||||
|
||||
/* in case already recording, stop recording and clear buffer queue */
|
||||
// in case already recording, stop recording and clear buffer queue
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record set state failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* enqueue empty buffers to be filled by the recorder */
|
||||
// enqueue empty buffers to be filled by the recorder
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], _this->spec.size);
|
||||
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], device->buffer_size);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record enqueue buffers failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
/* start recording */
|
||||
// start recording
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record set state failed: %d", result);
|
||||
|
@ -367,7 +366,7 @@ failed:
|
|||
return SDL_SetError("Open device failed!");
|
||||
}
|
||||
|
||||
/* this callback handler is called every time a buffer finishes playing */
|
||||
// this callback handler is called every time a buffer finishes playing
|
||||
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
|
||||
|
@ -376,20 +375,19 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
|||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_DestroyPCMPlayer(SDL_AudioDevice *_this)
|
||||
static void openslES_DestroyPCMPlayer(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLresult result;
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
/* set the player's state to 'stopped' */
|
||||
// set the player's state to 'stopped'
|
||||
if (bqPlayerPlay != NULL) {
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
|
||||
const SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SetPlayState stopped failed: %d", result);
|
||||
}
|
||||
}
|
||||
|
||||
/* destroy buffer queue audio player object, and invalidate all associated interfaces */
|
||||
// destroy buffer queue audio player object, and invalidate all associated interfaces
|
||||
if (bqPlayerObject != NULL) {
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
|
||||
|
@ -408,26 +406,14 @@ static void openslES_DestroyPCMPlayer(SDL_AudioDevice *_this)
|
|||
}
|
||||
}
|
||||
|
||||
static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
|
||||
static int openslES_CreatePCMPlayer(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
SLDataFormat_PCM format_pcm;
|
||||
SLAndroidDataFormat_PCM_EX format_pcm_ex;
|
||||
SLDataSource audioSrc;
|
||||
SLDataSink audioSnk;
|
||||
SLDataLocator_OutputMix loc_outmix;
|
||||
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
|
||||
SLresult result;
|
||||
int i;
|
||||
|
||||
/* If we want to add floating point audio support (requires API level 21)
|
||||
it can be done as described here:
|
||||
https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
|
||||
*/
|
||||
if (SDL_GetAndroidSDKVersion() >= 21) {
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
SDL_AudioFormat test_format;
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (SDL_AUDIO_ISSIGNED(test_format)) {
|
||||
|
@ -436,40 +422,42 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
|
|||
}
|
||||
|
||||
if (!test_format) {
|
||||
/* Didn't find a compatible format : */
|
||||
// Didn't find a compatible format :
|
||||
LOGI("No compatible audio format, using signed 16-bit audio");
|
||||
test_format = SDL_AUDIO_S16SYS;
|
||||
}
|
||||
_this->spec.format = test_format;
|
||||
device->spec.format = test_format;
|
||||
} else {
|
||||
/* Just go with signed 16-bit audio as it's the most compatible */
|
||||
_this->spec.format = SDL_AUDIO_S16SYS;
|
||||
// Just go with signed 16-bit audio as it's the most compatible
|
||||
device->spec.format = SDL_AUDIO_S16SYS;
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
// Update the fragment size as size in bytes
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
LOGI("Try to open %u hz %s %u bit chan %u %s samples %u",
|
||||
_this->spec.freq, SDL_AUDIO_ISFLOAT(_this->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(_this->spec.format),
|
||||
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
|
||||
device->spec.freq, SDL_AUDIO_ISFLOAT(device->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(device->spec.format),
|
||||
device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
|
||||
|
||||
/* configure audio source */
|
||||
// configure audio source
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
loc_bufq.numBuffers = NUM_BUFFERS;
|
||||
|
||||
SLDataFormat_PCM format_pcm;
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = _this->spec.channels;
|
||||
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */
|
||||
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
format_pcm.numChannels = device->spec.channels;
|
||||
format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
|
||||
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
|
||||
if (SDL_AUDIO_ISBIGENDIAN(_this->spec.format)) {
|
||||
if (SDL_AUDIO_ISBIGENDIAN(device->spec.format)) {
|
||||
format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
|
||||
} else {
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
}
|
||||
|
||||
switch (_this->spec.channels) {
|
||||
switch (device->spec.channels) {
|
||||
case 1:
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
|
||||
break;
|
||||
|
@ -495,14 +483,19 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
|
|||
format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1;
|
||||
break;
|
||||
default:
|
||||
/* Unknown number of channels, fall back to stereo */
|
||||
_this->spec.channels = 2;
|
||||
// Unknown number of channels, fall back to stereo
|
||||
device->spec.channels = 2;
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (SDL_AUDIO_ISFLOAT(_this->spec.format)) {
|
||||
/* Copy all setup into PCM EX structure */
|
||||
SLDataSink audioSnk;
|
||||
SLDataSource audioSrc;
|
||||
audioSrc.pFormat = (void *)&format_pcm;
|
||||
|
||||
SLAndroidDataFormat_PCM_EX format_pcm_ex;
|
||||
if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
|
||||
// Copy all setup into PCM EX structure
|
||||
format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
|
||||
format_pcm_ex.endianness = format_pcm.endianness;
|
||||
format_pcm_ex.channelMask = format_pcm.channelMask;
|
||||
|
@ -511,81 +504,87 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
|
|||
format_pcm_ex.bitsPerSample = format_pcm.bitsPerSample;
|
||||
format_pcm_ex.containerSize = format_pcm.containerSize;
|
||||
format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
||||
audioSrc.pFormat = (void *)&format_pcm_ex;
|
||||
}
|
||||
|
||||
audioSrc.pLocator = &loc_bufq;
|
||||
audioSrc.pFormat = SDL_AUDIO_ISFLOAT(_this->spec.format) ? (void *)&format_pcm_ex : (void *)&format_pcm;
|
||||
|
||||
/* configure audio sink */
|
||||
// configure audio sink
|
||||
SLDataLocator_OutputMix loc_outmix;
|
||||
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
loc_outmix.outputMix = outputMixObject;
|
||||
audioSnk.pLocator = &loc_outmix;
|
||||
audioSnk.pFormat = NULL;
|
||||
|
||||
/* create audio player */
|
||||
// create audio player
|
||||
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
|
||||
SLresult result;
|
||||
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* realize the player */
|
||||
// realize the player
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get the play interface */
|
||||
// get the play interface
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_PLAY interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get the buffer queue interface */
|
||||
// get the buffer queue interface
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* register callback on the buffer queue */
|
||||
/* context is '(SDL_PrivateAudioData *)_this->hidden' */
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, _this->hidden);
|
||||
// register callback on the buffer queue
|
||||
// context is '(SDL_PrivateAudioData *)device->hidden'
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, device->hidden);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RegisterCallback failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* get the volume interface */
|
||||
// get the volume interface
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_VOLUME interface get failed: %d", result);
|
||||
/* goto failed; */
|
||||
// goto failed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create the audio buffer semaphore */
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
// Create the audio buffer semaphore
|
||||
audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1);
|
||||
if (!audiodata->playsem) {
|
||||
LOGE("cannot create Semaphore!");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Create the sound buffers */
|
||||
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size);
|
||||
// Create the sound buffers
|
||||
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
|
||||
if (audiodata->mixbuff == NULL) {
|
||||
LOGE("mixbuffer allocate - out of memory");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
audiodata->pmixbuff[i] = audiodata->mixbuff + i * _this->spec.size;
|
||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||
audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size;
|
||||
}
|
||||
|
||||
/* set the player's state to playing */
|
||||
// set the player's state to playing
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Play set state failed: %d", result);
|
||||
|
@ -598,103 +597,98 @@ failed:
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int openslES_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
static int openslES_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
LOGI("openslES_OpenDevice() %s for capture", devname);
|
||||
return openslES_CreatePCMRecorder(_this);
|
||||
if (device->iscapture) {
|
||||
LOGI("openslES_OpenDevice() for capture");
|
||||
return openslES_CreatePCMRecorder(device);
|
||||
} else {
|
||||
int ret;
|
||||
LOGI("openslES_OpenDevice() %s for playing", devname);
|
||||
ret = openslES_CreatePCMPlayer(_this);
|
||||
LOGI("openslES_OpenDevice() for playing");
|
||||
ret = openslES_CreatePCMPlayer(device);
|
||||
if (ret < 0) {
|
||||
/* Another attempt to open the device with a lower frequency */
|
||||
if (_this->spec.freq > 48000) {
|
||||
openslES_DestroyPCMPlayer(_this);
|
||||
_this->spec.freq = 48000;
|
||||
ret = openslES_CreatePCMPlayer(_this);
|
||||
// Another attempt to open the device with a lower frequency
|
||||
if (device->spec.freq > 48000) {
|
||||
openslES_DestroyPCMPlayer(device);
|
||||
device->spec.freq = 48000;
|
||||
ret = openslES_CreatePCMPlayer(device);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
if (ret != 0) {
|
||||
return SDL_SetError("Open device failed!");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void openslES_WaitDevice(SDL_AudioDevice *_this)
|
||||
static void openslES_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
LOGV("openslES_WaitDevice()");
|
||||
|
||||
/* Wait for an audio chunk to finish */
|
||||
// Wait for an audio chunk to finish
|
||||
SDL_WaitSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_PlayDevice(SDL_AudioDevice *_this)
|
||||
static void openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLresult result;
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
LOGV("======openslES_PlayDevice()======");
|
||||
|
||||
/* Queue it up */
|
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
|
||||
// Queue it up
|
||||
const SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, buflen);
|
||||
|
||||
audiodata->next_buffer++;
|
||||
if (audiodata->next_buffer >= NUM_BUFFERS) {
|
||||
audiodata->next_buffer = 0;
|
||||
}
|
||||
|
||||
/* If Enqueue fails, callback won't be called.
|
||||
* Post the semaphore, not to run out of buffer */
|
||||
// If Enqueue fails, callback won't be called.
|
||||
// Post the semaphore, not to run out of buffer
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
}
|
||||
|
||||
/*/ n playn sem */
|
||||
/* getbuf 0 - 1 */
|
||||
/* fill buff 0 - 1 */
|
||||
/* play 0 - 0 1 */
|
||||
/* wait 1 0 0 */
|
||||
/* getbuf 1 0 0 */
|
||||
/* fill buff 1 0 0 */
|
||||
/* play 0 0 0 */
|
||||
/* wait */
|
||||
/* */
|
||||
/* okay.. */
|
||||
/// n playn sem
|
||||
// getbuf 0 - 1
|
||||
// fill buff 0 - 1
|
||||
// play 0 - 0 1
|
||||
// wait 1 0 0
|
||||
// getbuf 1 0 0
|
||||
// fill buff 1 0 0
|
||||
// play 0 0 0
|
||||
// wait
|
||||
//
|
||||
// okay..
|
||||
|
||||
static Uint8 *openslES_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
static Uint8 *openslES_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
LOGV("openslES_GetDeviceBuf()");
|
||||
return audiodata->pmixbuff[audiodata->next_buffer];
|
||||
}
|
||||
|
||||
static int openslES_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
static int openslES_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLresult result;
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
/* Wait for new recorded data */
|
||||
SDL_WaitSemaphore(audiodata->playsem);
|
||||
// Copy it to the output buffer
|
||||
SDL_assert(buflen == device->buffer_size);
|
||||
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
|
||||
|
||||
/* Copy it to the output buffer */
|
||||
SDL_assert(buflen == _this->spec.size);
|
||||
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
|
||||
|
||||
/* Re-enqueue the buffer */
|
||||
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
|
||||
// Re-enqueue the buffer
|
||||
const SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record enqueue buffers failed: %d", result);
|
||||
return -1;
|
||||
|
@ -705,22 +699,24 @@ static int openslES_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int
|
|||
audiodata->next_buffer = 0;
|
||||
}
|
||||
|
||||
return _this->spec.size;
|
||||
return device->buffer_size;
|
||||
}
|
||||
|
||||
static void openslES_CloseDevice(SDL_AudioDevice *_this)
|
||||
static void openslES_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
/* struct SDL_PrivateAudioData *audiodata = _this->hidden; */
|
||||
|
||||
if (_this->iscapture) {
|
||||
// struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
if (device->hidden) {
|
||||
if (device->iscapture) {
|
||||
LOGI("openslES_CloseDevice() for capture");
|
||||
openslES_DestroyPCMRecorder(_this);
|
||||
openslES_DestroyPCMRecorder(device);
|
||||
} else {
|
||||
LOGI("openslES_CloseDevice() for playing");
|
||||
openslES_DestroyPCMPlayer(_this);
|
||||
openslES_DestroyPCMPlayer(device);
|
||||
}
|
||||
|
||||
SDL_free(_this->hidden);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl)
|
||||
|
@ -733,24 +729,26 @@ static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl)
|
|||
|
||||
LOGI("openslES_Init() - set pointers");
|
||||
|
||||
/* Set the function pointers */
|
||||
/* impl->DetectDevices = openslES_DetectDevices; */
|
||||
// Set the function pointers
|
||||
// impl->DetectDevices = openslES_DetectDevices;
|
||||
impl->ThreadInit = Android_AudioThreadInit;
|
||||
impl->OpenDevice = openslES_OpenDevice;
|
||||
impl->WaitDevice = openslES_WaitDevice;
|
||||
impl->PlayDevice = openslES_PlayDevice;
|
||||
impl->GetDeviceBuf = openslES_GetDeviceBuf;
|
||||
impl->WaitCaptureDevice = openslES_WaitDevice;
|
||||
impl->CaptureFromDevice = openslES_CaptureFromDevice;
|
||||
impl->CloseDevice = openslES_CloseDevice;
|
||||
impl->Deinitialize = openslES_DestroyEngine;
|
||||
|
||||
/* and the capabilities */
|
||||
// and the capabilities
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
|
||||
LOGI("openslES_Init() - success");
|
||||
|
||||
/* this audio target is available. */
|
||||
// this audio target is available.
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
|
@ -761,7 +759,7 @@ AudioBootStrap openslES_bootstrap = {
|
|||
void openslES_ResumeDevices(void)
|
||||
{
|
||||
if (bqPlayerPlay != NULL) {
|
||||
/* set the player's state to 'playing' */
|
||||
// set the player's state to 'playing'
|
||||
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("openslES_ResumeDevices failed: %d", result);
|
||||
|
@ -772,7 +770,7 @@ void openslES_ResumeDevices(void)
|
|||
void openslES_PauseDevices(void)
|
||||
{
|
||||
if (bqPlayerPlay != NULL) {
|
||||
/* set the player's state to 'paused' */
|
||||
// set the player's state to 'paused'
|
||||
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("openslES_PauseDevices failed: %d", result);
|
||||
|
@ -780,4 +778,4 @@ void openslES_PauseDevices(void)
|
|||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_OPENSLES */
|
||||
#endif // SDL_AUDIO_DRIVER_OPENSLES
|
||||
|
|
|
@ -86,7 +86,10 @@ void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
|
|||
if (pAvSetMmThreadCharacteristicsW) {
|
||||
DWORD idx = 0;
|
||||
device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx);
|
||||
} else {
|
||||
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)
|
||||
|
|
|
@ -334,6 +334,7 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *device)
|
|||
void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
// !!! FIXME: set this thread to "Pro Audio" priority.
|
||||
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)
|
||||
|
|
|
@ -233,7 +233,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
|
|||
JNIEnv *env, jclass jcls);
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
|
||||
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture, jstring name,
|
||||
jint device_id);
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
|
@ -242,7 +242,7 @@ JNIEXPORT void JNICALL
|
|||
|
||||
static JNINativeMethod SDLAudioManager_tab[] = {
|
||||
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) },
|
||||
{ "addAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
|
||||
{ "addAudioDevice", "(ZLjava/lang/String;I)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
|
||||
{ "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) }
|
||||
};
|
||||
|
||||
|
@ -350,8 +350,8 @@ static jmethodID midSupportsRelativeMouse;
|
|||
static jclass mAudioManagerClass;
|
||||
|
||||
/* method signatures */
|
||||
static jmethodID midGetAudioOutputDevices;
|
||||
static jmethodID midGetAudioInputDevices;
|
||||
static jmethodID midRegisterAudioDeviceCallback;
|
||||
static jmethodID midUnregisterAudioDeviceCallback;
|
||||
static jmethodID midAudioOpen;
|
||||
static jmethodID midAudioWriteByteBuffer;
|
||||
static jmethodID midAudioWriteShortBuffer;
|
||||
|
@ -681,12 +681,12 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
|
|||
|
||||
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
|
||||
|
||||
midGetAudioOutputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"getAudioOutputDevices",
|
||||
"()[I");
|
||||
midGetAudioInputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"getAudioInputDevices",
|
||||
"()[I");
|
||||
midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"registerAudioDeviceCallback",
|
||||
"()V");
|
||||
midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"unregisterAudioDeviceCallback",
|
||||
"()V");
|
||||
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"audioOpen", "(IIIII)[I");
|
||||
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
|
@ -710,7 +710,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
|
|||
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"audioSetThreadPriority", "(ZI)V");
|
||||
|
||||
if (!midGetAudioOutputDevices || !midGetAudioInputDevices || !midAudioOpen ||
|
||||
if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioOpen ||
|
||||
!midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer ||
|
||||
!midAudioClose ||
|
||||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer ||
|
||||
|
@ -1002,19 +1002,14 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
|
|||
SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE);
|
||||
}
|
||||
|
||||
extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle);
|
||||
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle);
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
|
||||
jint device_id)
|
||||
jstring name, jint device_id)
|
||||
{
|
||||
if (SDL_GetCurrentAudioDriver() != NULL) {
|
||||
char device_name[64];
|
||||
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
|
||||
SDL_Log("Adding device with name %s, capture %d", device_name, is_capture);
|
||||
SDL_AddAudioDevice(is_capture, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
|
||||
}
|
||||
SDL_assert(SDL_GetCurrentAudioDriver() != NULL); // should have been started by Android_StartAudioHotplug inside an audio driver.
|
||||
const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL);
|
||||
SDL_AddAudioDevice(is_capture ? SDL_TRUE : SDL_FALSE, SDL_strdup(utf8name), NULL, (void *)((size_t)device_id));
|
||||
(*env)->ReleaseStringUTFChars(env, name, utf8name);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
|
@ -1022,8 +1017,8 @@ SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean i
|
|||
jint device_id)
|
||||
{
|
||||
if (SDL_GetCurrentAudioDriver() != NULL) {
|
||||
SDL_Log("Removing device with handle %d, capture %d", device_id + 1, is_capture);
|
||||
SDL_RemoveAudioDevice(is_capture, (void *)((size_t)device_id + 1));
|
||||
SDL_Log("Removing device with handle %d, capture %d", device_id, is_capture);
|
||||
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1564,58 +1559,25 @@ static void *audioBufferPinned = NULL;
|
|||
static int captureBufferFormat = 0;
|
||||
static jobject captureBuffer = NULL;
|
||||
|
||||
static void Android_JNI_GetAudioDevices(int *devices, int *length, int max_len, int is_input)
|
||||
void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
JNIEnv *env = Android_JNI_GetEnv();
|
||||
jintArray result;
|
||||
|
||||
if (is_input) {
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioInputDevices);
|
||||
} else {
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioOutputDevices);
|
||||
// this will fire the callback for each existing device right away (which will eventually SDL_AddAudioDevice), and again later when things change.
|
||||
(void) (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midRegisterAudioDeviceCallback);
|
||||
*default_output = *default_capture = NULL; // !!! FIXME: how do you decide the default device id?
|
||||
}
|
||||
|
||||
*length = (*env)->GetArrayLength(env, result);
|
||||
|
||||
*length = SDL_min(*length, max_len);
|
||||
|
||||
(*env)->GetIntArrayRegion(env, result, 0, *length, devices);
|
||||
}
|
||||
|
||||
void Android_DetectDevices(void)
|
||||
void Android_StopAudioHotplug(void)
|
||||
{
|
||||
int inputs[100];
|
||||
int outputs[100];
|
||||
int inputs_length = 0;
|
||||
int outputs_length = 0;
|
||||
|
||||
SDL_zeroa(inputs);
|
||||
|
||||
Android_JNI_GetAudioDevices(inputs, &inputs_length, 100, 1 /* input devices */);
|
||||
|
||||
for (int i = 0; i < inputs_length; ++i) {
|
||||
int device_id = inputs[i];
|
||||
char device_name[64];
|
||||
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
|
||||
SDL_Log("Adding input device with name %s", device_name);
|
||||
SDL_AddAudioDevice(SDL_TRUE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
|
||||
JNIEnv *env = Android_JNI_GetEnv();
|
||||
(void) (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback);
|
||||
}
|
||||
|
||||
SDL_zeroa(outputs);
|
||||
|
||||
Android_JNI_GetAudioDevices(outputs, &outputs_length, 100, 0 /* output devices */);
|
||||
|
||||
for (int i = 0; i < outputs_length; ++i) {
|
||||
int device_id = outputs[i];
|
||||
char device_name[64];
|
||||
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
|
||||
SDL_Log("Adding output device with name %s", device_name);
|
||||
SDL_AddAudioDevice(SDL_FALSE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
|
||||
}
|
||||
}
|
||||
|
||||
int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec)
|
||||
int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
SDL_AudioSpec *spec = &device->spec;
|
||||
const int device_id = (int) ((size_t) device->handle);
|
||||
int audioformat;
|
||||
jobject jbufobj = NULL;
|
||||
jobject result;
|
||||
|
@ -1640,10 +1602,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
|||
|
||||
if (iscapture) {
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
|
||||
} else {
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
|
||||
}
|
||||
if (result == NULL) {
|
||||
/* Error during audio initialization, error printed from Java */
|
||||
|
@ -1668,10 +1630,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
|||
spec->format = SDL_AUDIO_F32;
|
||||
break;
|
||||
default:
|
||||
return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
|
||||
return SDL_SetError("Unexpected audio format from Java: %d", audioformat);
|
||||
}
|
||||
spec->channels = resultElements[2];
|
||||
spec->samples = resultElements[3];
|
||||
device->sample_frames = resultElements[3];
|
||||
(*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
|
||||
(*env)->DeleteLocalRef(env, result);
|
||||
|
||||
|
@ -1680,7 +1642,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
|||
switch (audioformat) {
|
||||
case ENCODING_PCM_8BIT:
|
||||
{
|
||||
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels);
|
||||
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, device->sample_frames * spec->channels);
|
||||
if (audioBufferLocal) {
|
||||
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||
|
@ -1688,7 +1650,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
|||
} break;
|
||||
case ENCODING_PCM_16BIT:
|
||||
{
|
||||
jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels);
|
||||
jshortArray audioBufferLocal = (*env)->NewShortArray(env, device->sample_frames * spec->channels);
|
||||
if (audioBufferLocal) {
|
||||
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||
|
@ -1696,7 +1658,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
|||
} break;
|
||||
case ENCODING_PCM_FLOAT:
|
||||
{
|
||||
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels);
|
||||
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, device->sample_frames * spec->channels);
|
||||
if (audioBufferLocal) {
|
||||
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||
|
@ -1887,12 +1849,17 @@ void Android_JNI_CloseAudioDevice(const int iscapture)
|
|||
}
|
||||
}
|
||||
|
||||
void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
|
||||
static void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
|
||||
{
|
||||
JNIEnv *env = Android_JNI_GetEnv();
|
||||
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id);
|
||||
}
|
||||
|
||||
void Android_AudioThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
Android_JNI_AudioSetThreadPriority((int) device->iscapture, (int)device->instance_id);
|
||||
}
|
||||
|
||||
/* Test for an exception and call SDL_SetError with its detail if one occurs */
|
||||
/* If the parameter silent is truthy then SDL_SetError() will not be called. */
|
||||
static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
|
||||
|
|
|
@ -30,6 +30,8 @@ extern "C" {
|
|||
#include <EGL/eglplatform.h>
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include "../../audio/SDL_sysaudio.h"
|
||||
|
||||
/* Interface from the SDL library into the Android Java activity */
|
||||
extern void Android_JNI_SetActivityTitle(const char *title);
|
||||
extern void Android_JNI_SetWindowStyle(SDL_bool fullscreen);
|
||||
|
@ -47,14 +49,15 @@ extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void);
|
|||
extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void);
|
||||
|
||||
/* Audio support */
|
||||
extern void Android_DetectDevices(void);
|
||||
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec);
|
||||
void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
|
||||
void Android_StopAudioHotplug(void);
|
||||
extern void Android_AudioThreadInit(SDL_AudioDevice *device);
|
||||
extern int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device);
|
||||
extern void *Android_JNI_GetAudioBuffer(void);
|
||||
extern void Android_JNI_WriteAudioBuffer(void);
|
||||
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
|
||||
extern void Android_JNI_FlushCapturedAudio(void);
|
||||
extern void Android_JNI_CloseAudioDevice(const int iscapture);
|
||||
extern void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id);
|
||||
|
||||
/* Detecting device type */
|
||||
extern SDL_bool Android_IsDeXMode(void);
|
||||
|
|
Loading…
Reference in New Issue