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.
This commit is contained in:
Ryan C. Gordon 2023-07-26 15:45:40 -04:00
parent 54af687210
commit 5ff87c6d4a
No known key found for this signature in database
GPG key ID: FA148B892AB48044
10 changed files with 485 additions and 720 deletions

View file

@ -21,8 +21,6 @@ public class SDLAudioManager {
protected static AudioRecord mAudioRecord; protected static AudioRecord mAudioRecord;
protected static Context mContext; protected static Context mContext;
private static final int[] NO_DEVICES = {};
private static AudioDeviceCallback mAudioDeviceCallback; private static AudioDeviceCallback mAudioDeviceCallback;
public static void initialize() { public static void initialize() {
@ -36,7 +34,7 @@ public class SDLAudioManager {
@Override @Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
for (AudioDeviceInfo deviceInfo : 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) { public static void setContext(Context context) {
mContext = context; mContext = context;
if (context != null) {
registerAudioDeviceCallback();
}
} }
public static void release(Context context) { public static void release(Context context) {
unregisterAudioDeviceCallback(context); // no-op atm
} }
// Audio // Audio
@ -311,65 +306,20 @@ public class SDLAudioManager {
return null; return null;
} }
private static void registerAudioDeviceCallback() { public static void registerAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null); audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
} }
} }
private static void unregisterAudioDeviceCallback(Context context) { public static void unregisterAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { 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); 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. * 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 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);
} }

View file

@ -25,8 +25,6 @@
#include "../thread/SDL_systhread.h" #include "../thread/SDL_systhread.h"
#include "../SDL_utils_c.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 // Available audio drivers
static const AudioBootStrap *const bootstrap[] = { static const AudioBootStrap *const bootstrap[] = {
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO #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... // 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_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioWaitDevice_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. */ } 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_AudioDeinitialize_Default(void) { /* no-op. */ }
static void SDL_AudioFreeDeviceHandle_Default(SDL_AudioDevice *device) { /* 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) static void SDL_AudioDetectDevices_Default(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{ {
// you have to write your own implementation if these assertions fail. // 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) void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device)
{ {
SDL_assert(!device->iscapture); 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); current_audio.impl.ThreadInit(device);
} }
@ -781,14 +774,6 @@ static int SDLCALL OutputAudioThread(void *devicep) // thread entry point
void SDL_CaptureAudioThreadSetup(SDL_AudioDevice *device) void SDL_CaptureAudioThreadSetup(SDL_AudioDevice *device)
{ {
SDL_assert(device->iscapture); 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); current_audio.impl.ThreadInit(device);
} }
@ -1068,7 +1053,6 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
if ((devid == 0) && is_default) { if ((devid == 0) && is_default) {
return SDL_SetError("No default audio device available"); return SDL_SetError("No default audio device available");
return 0;
} }
SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid); SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid);

View file

@ -34,16 +34,13 @@ struct SDL_PrivateAudioData
{ {
AAudioStream *stream; AAudioStream *stream;
/* Raw mixing buffer */ Uint8 *mixbuf; // Raw mixing buffer
Uint8 *mixbuf;
int mixlen;
int frame_size; int frame_size;
/* Resume device if it was paused automatically */ int resume; // Resume device if it was paused automatically
int resume;
}; };
/* Debug */ // Debug
#if 0 #if 0
#define LOGI(...) SDL_Log(__VA_ARGS__); #define LOGI(...) SDL_Log(__VA_ARGS__);
#else #else
@ -52,7 +49,6 @@ struct SDL_PrivateAudioData
typedef struct AAUDIO_Data typedef struct AAUDIO_Data
{ {
AAudioStreamBuilder *builder;
void *handle; void *handle;
#define SDL_PROC(ret, func, params) ret (*func) params; #define SDL_PROC(ret, func, params) ret (*func) params;
#include "SDL_aaudiofuncs.h" #include "SDL_aaudiofuncs.h"
@ -79,11 +75,14 @@ static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_re
#define LIB_AAUDIO_SO "libaaudio.so" #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; struct SDL_PrivateAudioData *hidden;
SDL_bool iscapture = _this->iscapture; const SDL_bool iscapture = device->iscapture;
aaudio_result_t res; aaudio_result_t res;
SDL_assert(device->handle != NULL); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero.
LOGI(__func__); LOGI(__func__);
if (iscapture) { 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)); hidden = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (_this->hidden == NULL) { if (hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
private = _this->hidden;
ctx.AAudioStreamBuilder_setSampleRate(ctx.builder, _this->spec.freq); AAudioStreamBuilder *builder = NULL;
ctx.AAudioStreamBuilder_setChannelCount(ctx.builder, _this->spec.channels); res = ctx.AAudio_createStreamBuilder(&builder);
if(devname != NULL) { if (res != AAUDIO_OK) {
int aaudio_device_id = SDL_atoi(devname); LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
LOGI("Opening device id %d", aaudio_device_id); return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res);
ctx.AAudioStreamBuilder_setDeviceId(ctx.builder, aaudio_device_id); } else if (builder == NULL) {
} LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
{ return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL");
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) {
format = AAUDIO_FORMAT_PCM_FLOAT;
}
ctx.AAudioStreamBuilder_setFormat(ctx.builder, format);
} }
ctx.AAudioStreamBuilder_setErrorCallback(ctx.builder, AAUDIO_errorCallback, private); // !!! 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(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;
} else {
format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else.
}
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", LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format), device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples); 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) { if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res); LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
} }
_this->spec.freq = ctx.AAudioStream_getSampleRate(private->stream); device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream);
_this->spec.channels = ctx.AAudioStream_getChannelCount(private->stream); device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream);
{
aaudio_format_t fmt = ctx.AAudioStream_getFormat(private->stream); format = ctx.AAudioStream_getFormat(hidden->stream);
if (fmt == AAUDIO_FORMAT_PCM_I16) { if (format == AAUDIO_FORMAT_PCM_I16) {
_this->spec.format = SDL_AUDIO_S16SYS; device->spec.format = SDL_AUDIO_S16SYS;
} else if (fmt == AAUDIO_FORMAT_PCM_FLOAT) { } else if (format == AAUDIO_FORMAT_PCM_I32) {
_this->spec.format = SDL_AUDIO_F32SYS; 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", LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format), device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples); 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) { if (!iscapture) {
private->mixlen = _this->spec.size; hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
private->mixbuf = (Uint8 *)SDL_malloc(private->mixlen); if (hidden->mixbuf == NULL) {
if (private->mixbuf == NULL) {
return SDL_OutOfMemory(); 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) { if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture); LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture);
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); 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; return 0;
} }
static void AAUDIO_CloseDevice(SDL_AudioDevice *_this) static void AAUDIO_CloseDevice(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *private = _this->hidden; struct SDL_PrivateAudioData *hidden = device->hidden;
aaudio_result_t res; if (hidden) {
LOGI(__func__); LOGI(__func__);
if (private->stream) { if (hidden->stream) {
res = ctx.AAudioStream_requestStop(private->stream); ctx.AAudioStream_requestStop(hidden->stream);
if (res != AAUDIO_OK) { ctx.AAudioStream_close(hidden->stream);
LOGI("SDL Failed AAudioStream_requestStop %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return;
} }
res = ctx.AAudioStream_close(private->stream); SDL_free(hidden->mixbuf);
if (res != AAUDIO_OK) { SDL_free(hidden);
LOGI("SDL Failed AAudioStreamBuilder_delete %d", res); device->hidden = NULL;
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return;
}
} }
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 device->hidden->mixbuf;
return private->mixbuf;
} }
static void AAUDIO_PlayDevice(SDL_AudioDevice *_this) static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *private = _this->hidden; AAudioStream *stream = device->hidden->stream;
aaudio_result_t res; while (!SDL_AtomicGet(&device->shutdown) && ((int) ctx.AAudioStream_getBufferSizeInFrames(stream)) < device->sample_frames) {
int64_t timeoutNanoseconds = 1 * 1000 * 1000; /* 8 ms */ SDL_Delay(1);
res = ctx.AAudioStream_write(private->stream, private->mixbuf, private->mixlen / private->frame_size, timeoutNanoseconds); }
}
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) { if (res < 0) {
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
} else { } 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 #if 0
/* Log under-run count */ // Log under-run count
{ {
static int prev = 0; static int prev = 0;
int32_t cnt = ctx.AAudioStream_getXRunCount(private->stream); int32_t cnt = ctx.AAudioStream_getXRunCount(hidden->stream);
if (cnt != prev) { if (cnt != prev) {
SDL_Log("AAudio underrun: %d - total: %d", cnt - prev, cnt); SDL_Log("AAudio underrun: %d - total: %d", cnt - prev, cnt);
prev = cnt; prev = cnt;
@ -228,41 +239,31 @@ static void AAUDIO_PlayDevice(SDL_AudioDevice *_this)
#endif #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; const aaudio_result_t res = ctx.AAudioStream_read(device->hidden->stream, buffer, device->sample_frames, 0);
aaudio_result_t res;
int64_t timeoutNanoseconds = 8 * 1000 * 1000; /* 8 ms */
res = ctx.AAudioStream_read(private->stream, buffer, buflen / private->frame_size, timeoutNanoseconds);
if (res < 0) { if (res < 0) {
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return -1; return -1;
} }
LOGI("SDL AAudio capture:%d frames, wanted:%d frames", (int)res, buflen / private->frame_size); LOGI("SDL AAudio capture:%d frames, wanted:%d frames", (int)res, buflen / device->hidden->frame_size);
return res * private->frame_size; return res * device->hidden->frame_size;
} }
static void AAUDIO_Deinitialize(void) static void AAUDIO_Deinitialize(void)
{ {
Android_StopAudioHotplug();
LOGI(__func__); LOGI(__func__);
if (ctx.handle) { 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); SDL_UnloadObject(ctx.handle);
} }
ctx.handle = NULL; SDL_zero(ctx);
ctx.builder = NULL;
LOGI("End AAUDIO %s", SDL_GetError()); LOGI("End AAUDIO %s", SDL_GetError());
} }
static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
aaudio_result_t res;
LOGI(__func__); LOGI(__func__);
/* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release, /* 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); ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO);
if (ctx.handle == NULL) { if (ctx.handle == NULL) {
LOGI("SDL couldn't find " LIB_AAUDIO_SO); LOGI("SDL couldn't find " LIB_AAUDIO_SO);
goto failure; return SDL_FALSE;
} }
if (AAUDIO_LoadFunctions(&ctx) < 0) { if (AAUDIO_LoadFunctions(&ctx) < 0) {
goto failure; SDL_UnloadObject(ctx.handle);
SDL_zero(ctx);
return SDL_FALSE;
} }
res = ctx.AAudio_createStreamBuilder(&ctx.builder); impl->ThreadInit = Android_AudioThreadInit;
if (res != AAUDIO_OK) { impl->DetectDevices = Android_StartAudioHotplug;
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->Deinitialize = AAUDIO_Deinitialize; impl->Deinitialize = AAUDIO_Deinitialize;
impl->OpenDevice = AAUDIO_OpenDevice; impl->OpenDevice = AAUDIO_OpenDevice;
impl->CloseDevice = AAUDIO_CloseDevice; impl->CloseDevice = AAUDIO_CloseDevice;
impl->WaitDevice = AAUDIO_WaitDevice;
impl->PlayDevice = AAUDIO_PlayDevice; impl->PlayDevice = AAUDIO_PlayDevice;
impl->GetDeviceBuf = AAUDIO_GetDeviceBuf; impl->GetDeviceBuf = AAUDIO_GetDeviceBuf;
impl->WaitCaptureDevice = AAUDIO_WaitDevice;
impl->CaptureFromDevice = AAUDIO_CaptureFromDevice; impl->CaptureFromDevice = AAUDIO_CaptureFromDevice;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
/* this audio target is available. */
LOGI("SDL AAUDIO_Init OK"); LOGI("SDL AAUDIO_Init OK");
return SDL_TRUE; 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 = { AudioBootStrap AAUDIO_bootstrap = {
"AAudio", "AAudio audio driver", AAUDIO_Init, SDL_FALSE "AAudio", "AAudio audio driver", AAUDIO_Init, SDL_FALSE
}; };
/* Pause (block) all non already paused audio devices by taking their mixer lock */
static SDL_bool PauseOneDevice(SDL_AudioDevice *device, void *userdata)
{
struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden;
if (hidden != NULL) {
if (hidden->stream) {
aaudio_result_t res;
if (device->iscapture) {
// Pause() isn't implemented for 'capture', use Stop()
res = ctx.AAudioStream_requestStop(hidden->stream);
} else {
res = ctx.AAudioStream_requestPause(hidden->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.
}
// Pause (block) all non already paused audio devices by taking their mixer lock
void AAUDIO_PauseDevices(void) void AAUDIO_PauseDevices(void)
{ {
int i; if (ctx.handle != NULL) { // AAUDIO driver is used?
(void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL);
/* 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 = (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));
}
}
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;
}
}
if (captureDevice != NULL && captureDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (private->stream) {
/* Pause() isn't implemented for 'capture', use Stop() */
aaudio_result_t 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));
}
}
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;
}
}
} }
} }
/* 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
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 (hidden->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
}
return SDL_FALSE; // keep enumerating.
}
void AAUDIO_ResumeDevices(void) void AAUDIO_ResumeDevices(void)
{ {
int i; if (ctx.handle != NULL) { // AAUDIO driver is used?
(void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL);
/* 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));
}
}
}
} }
} }
@ -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. 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 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; SDL_assert(device != NULL);
if (!device->iscapture && device->hidden != NULL) {
/* AAUDIO driver is not used */ struct SDL_PrivateAudioData *hidden = device->hidden;
if (ctx.handle == NULL) { int64_t framePosition, timeNanoseconds;
return SDL_FALSE; 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(hidden->stream);
for (i = 0; i < get_max_num_audio_dev(); i++) { // 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.
SDL_AudioDevice *_this = get_audio_dev(i); if (currentState == AAUDIO_STREAM_STATE_STARTED) {
SDL_AudioDevice *audioDevice = NULL; 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);
SDL_AudioDevice *captureDevice = NULL; return SDL_TRUE; // this guy.
if (_this == NULL) {
continue;
}
if (_this->iscapture) {
captureDevice = _this;
} else {
audioDevice = _this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
int64_t framePosition, timeNanoseconds;
aaudio_result_t res = ctx.AAudioStream_getTimestamp(private->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. */
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;
}
} }
} }
(void) captureDevice;
} }
return SDL_FALSE; return SDL_FALSE; // enumerate more devices.
} }
#endif /* SDL_AUDIO_DRIVER_AAUDIO */ SDL_bool AAUDIO_DetectBrokenPlayState(void)
{
return (ctx.handle && SDL_FindPhysicalAudioDeviceByCallback(DetectBrokenPlayStatePerDevice, NULL) != NULL) ? SDL_TRUE : SDL_FALSE;
}
#endif // SDL_AUDIO_DRIVER_AAUDIO

View file

@ -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_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(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(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_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_UNUSED(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream)) SDL_PROC(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream)) SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream))

View file

@ -22,7 +22,7 @@
#ifdef SDL_AUDIO_DRIVER_ANDROID #ifdef SDL_AUDIO_DRIVER_ANDROID
/* Output audio to Android */ // Output audio to Android (legacy interface)
#include "../SDL_sysaudio.h" #include "../SDL_sysaudio.h"
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
@ -34,185 +34,157 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
/* Resume device if it was paused automatically */ int resume; // Resume device if it was paused automatically
int resume;
}; };
static SDL_AudioDevice *audioDevice = NULL; static SDL_AudioDevice *audioDevice = NULL;
static SDL_AudioDevice *captureDevice = NULL; static SDL_AudioDevice *captureDevice = NULL;
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *device)
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
{ {
SDL_AudioFormat test_format; device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
const SDL_AudioFormat *closefmts; if (device->hidden == NULL) {
SDL_bool iscapture = _this->iscapture; return SDL_OutOfMemory();
}
const SDL_bool iscapture = device->iscapture;
if (iscapture) { if (iscapture) {
if (captureDevice) { if (captureDevice) {
return SDL_SetError("An audio capture device is already opened"); return SDL_SetError("An audio capture device is already opened");
} }
} captureDevice = device;
} else {
if (!iscapture) {
if (audioDevice) { if (audioDevice) {
return SDL_SetError("An audio playback device is already opened"); return SDL_SetError("An audio playback device is already opened");
} }
audioDevice = device;
} }
if (iscapture) { SDL_AudioFormat test_format;
captureDevice = _this; const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
} 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);
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
if ((test_format == SDL_AUDIO_U8) || if ((test_format == SDL_AUDIO_U8) ||
(test_format == SDL_AUDIO_S16) || (test_format == SDL_AUDIO_S16) ||
(test_format == SDL_AUDIO_F32)) { (test_format == SDL_AUDIO_F32)) {
_this->spec.format = test_format; device->spec.format = test_format;
break; break;
} }
} }
if (!test_format) { if (!test_format) {
/* Didn't find a compatible format :( */ return SDL_SetError("android: Unsupported audio format");
return SDL_SetError("%s: Unsupported audio format", "android");
} }
{ if (Android_JNI_OpenAudioDevice(device) < 0) {
int audio_device_id = 0; return -1;
if (devname != NULL) {
audio_device_id = SDL_atoi(devname);
}
if (Android_JNI_OpenAudioDevice(iscapture, audio_device_id, &_this->spec) < 0) {
return -1;
}
} }
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
return 0; 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(); Android_JNI_WriteAudioBuffer();
} }
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
return Android_JNI_GetAudioBuffer(); 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); return Android_JNI_CaptureAudioBuffer(buffer, buflen);
} }
static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *_this) static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *device)
{ {
Android_JNI_FlushCapturedAudio(); 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 /* 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 so it's safe to terminate the Java side buffer and AudioTrack
*/ */
Android_JNI_CloseAudioDevice(_this->iscapture); if (device->hidden) {
if (_this->iscapture) { Android_JNI_CloseAudioDevice(device->iscapture);
SDL_assert(captureDevice == _this); if (device->iscapture) {
captureDevice = NULL; SDL_assert(captureDevice == device);
} else { captureDevice = NULL;
SDL_assert(audioDevice == _this); } else {
audioDevice = NULL; SDL_assert(audioDevice == device);
audioDevice = NULL;
}
SDL_free(device->hidden);
device->hidden = NULL;
} }
SDL_free(_this->hidden);
} }
static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl) static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl)
{ {
/* Set the function pointers */ impl->ThreadInit = Android_AudioThreadInit;
impl->DetectDevices = Android_DetectDevices; impl->DetectDevices = Android_StartAudioHotplug;
impl->Deinitialize = Android_StopAudioHotplug;
impl->OpenDevice = ANDROIDAUDIO_OpenDevice; impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
impl->PlayDevice = ANDROIDAUDIO_PlayDevice; impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf; impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
impl->CloseDevice = ANDROIDAUDIO_CloseDevice; impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice; impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
impl->FlushCapture = ANDROIDAUDIO_FlushCapture; impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE; 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 = { AudioBootStrap ANDROIDAUDIO_bootstrap = {
"android", "SDL Android audio driver", ANDROIDAUDIO_Init, SDL_FALSE "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) void ANDROIDAUDIO_PauseDevices(void)
{ {
/* TODO: Handle multiple devices? */ // TODO: Handle multiple devices?
struct SDL_PrivateAudioData *private; struct SDL_PrivateAudioData *hidden;
if (audioDevice != NULL && audioDevice->hidden != NULL) { if (audioDevice != NULL && audioDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)audioDevice->hidden; hidden = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (SDL_AtomicGet(&audioDevice->paused)) { SDL_LockMutex(audioDevice->lock);
/* The device is already paused, leave it alone */ hidden->resume = SDL_TRUE;
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(audioDevice->mixer_lock);
SDL_AtomicSet(&audioDevice->paused, 1);
private->resume = SDL_TRUE;
}
} }
if (captureDevice != NULL && captureDevice->hidden != NULL) { if (captureDevice != NULL && captureDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)captureDevice->hidden; hidden = (struct SDL_PrivateAudioData *)captureDevice->hidden;
if (SDL_AtomicGet(&captureDevice->paused)) { SDL_LockMutex(captureDevice->lock);
/* The device is already paused, leave it alone */ hidden->resume = SDL_TRUE;
private->resume = SDL_FALSE;
} else {
SDL_LockMutex(captureDevice->mixer_lock);
SDL_AtomicSet(&captureDevice->paused, 1);
private->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) void ANDROIDAUDIO_ResumeDevices(void)
{ {
/* TODO: Handle multiple devices? */ // TODO: Handle multiple devices?
struct SDL_PrivateAudioData *private; struct SDL_PrivateAudioData *hidden;
if (audioDevice != NULL && audioDevice->hidden != NULL) { if (audioDevice != NULL && audioDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)audioDevice->hidden; hidden = (struct SDL_PrivateAudioData *)audioDevice->hidden;
if (private->resume) { if (hidden->resume) {
SDL_AtomicSet(&audioDevice->paused, 0); hidden->resume = SDL_FALSE;
private->resume = SDL_FALSE; SDL_UnlockMutex(audioDevice->lock);
SDL_UnlockMutex(audioDevice->mixer_lock);
} }
} }
if (captureDevice != NULL && captureDevice->hidden != NULL) { if (captureDevice != NULL && captureDevice->hidden != NULL) {
private = (struct SDL_PrivateAudioData *)captureDevice->hidden; hidden = (struct SDL_PrivateAudioData *)captureDevice->hidden;
if (private->resume) { if (hidden->resume) {
SDL_AtomicSet(&captureDevice->paused, 0); hidden->resume = SDL_FALSE;
private->resume = SDL_FALSE; SDL_UnlockMutex(captureDevice->lock);
SDL_UnlockMutex(captureDevice->mixer_lock);
} }
} }
} }
#endif /* SDL_AUDIO_DRIVER_ANDROID */ #endif // SDL_AUDIO_DRIVER_ANDROID

View file

@ -22,9 +22,8 @@
#ifdef SDL_AUDIO_DRIVER_OPENSLES #ifdef SDL_AUDIO_DRIVER_OPENSLES
/* For more discussion of low latency audio on Android, see this: // For more discussion of low latency audio on Android, see this:
https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html // https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
*/
#include "../SDL_sysaudio.h" #include "../SDL_sysaudio.h"
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
@ -36,7 +35,7 @@
#include <android/log.h> #include <android/log.h>
#define NUM_BUFFERS 2 /* -- Don't lower this! */ #define NUM_BUFFERS 2 // -- Don't lower this!
struct SDL_PrivateAudioData 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_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) #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 SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine = NULL; static SLEngineItf engineEngine = NULL;
/* output mix interfaces */ // output mix interfaces
static SLObjectItf outputMixObject = NULL; static SLObjectItf outputMixObject = NULL;
/* buffer queue player interfaces */ // buffer queue player interfaces
static SLObjectItf bqPlayerObject = NULL; static SLObjectItf bqPlayerObject = NULL;
static SLPlayItf bqPlayerPlay = NULL; static SLPlayItf bqPlayerPlay = NULL;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL; static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
@ -98,7 +97,7 @@ static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
static SLVolumeItf bqPlayerVolume; static SLVolumeItf bqPlayerVolume;
#endif #endif
/* recorder interfaces */ // recorder interfaces
static SLObjectItf recorderObject = NULL; static SLObjectItf recorderObject = NULL;
static SLRecordItf recorderRecord = NULL; static SLRecordItf recorderRecord = NULL;
static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL; static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
@ -123,13 +122,13 @@ static void openslES_DestroyEngine(void)
{ {
LOGI("openslES_DestroyEngine()"); LOGI("openslES_DestroyEngine()");
/* destroy output mix object, and invalidate all associated interfaces */ // destroy output mix object, and invalidate all associated interfaces
if (outputMixObject != NULL) { if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject); (*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL; outputMixObject = NULL;
} }
/* destroy engine object, and invalidate all associated interfaces */ // destroy engine object, and invalidate all associated interfaces
if (engineObject != NULL) { if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject); (*engineObject)->Destroy(engineObject);
engineObject = NULL; engineObject = NULL;
@ -145,7 +144,7 @@ static int openslES_CreateEngine(void)
LOGI("openSLES_CreateEngine()"); LOGI("openSLES_CreateEngine()");
/* create engine */ // create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("slCreateEngine failed: %d", result); LOGE("slCreateEngine failed: %d", result);
@ -153,7 +152,7 @@ static int openslES_CreateEngine(void)
} }
LOGI("slCreateEngine OK"); LOGI("slCreateEngine OK");
/* realize the engine */ // realize the engine
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeEngine failed: %d", result); LOGE("RealizeEngine failed: %d", result);
@ -161,7 +160,7 @@ static int openslES_CreateEngine(void)
} }
LOGI("RealizeEngine OK"); 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); result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("EngineGetInterface failed: %d", result); LOGE("EngineGetInterface failed: %d", result);
@ -169,7 +168,7 @@ static int openslES_CreateEngine(void)
} }
LOGI("EngineGetInterface OK"); LOGI("EngineGetInterface OK");
/* create output mix */ // create output mix
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req); result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("CreateOutputMix failed: %d", result); LOGE("CreateOutputMix failed: %d", result);
@ -177,7 +176,7 @@ static int openslES_CreateEngine(void)
} }
LOGI("CreateOutputMix OK"); LOGI("CreateOutputMix OK");
/* realize the output mix */ // realize the output mix
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeOutputMix failed: %d", result); LOGE("RealizeOutputMix failed: %d", result);
@ -190,7 +189,7 @@ error:
return 0; 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) static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context; struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
@ -199,12 +198,12 @@ static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
SDL_PostSemaphore(audiodata->playsem); 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; SLresult result;
/* stop recording */ // stop recording
if (recorderRecord != NULL) { if (recorderRecord != NULL) {
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED); result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) { 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) { if (recorderObject != NULL) {
(*recorderObject)->Destroy(recorderObject); (*recorderObject)->Destroy(recorderObject);
recorderObject = NULL; 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; SLDataFormat_PCM format_pcm;
SLDataLocator_AndroidSimpleBufferQueue loc_bufq; SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
SLDataSink audioSnk; SLDataSink audioSnk;
@ -248,19 +247,19 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
} }
/* Just go with signed 16-bit audio as it's the most compatible */ // Just go with signed 16-bit audio as it's the most compatible
_this->spec.format = SDL_AUDIO_S16SYS; device->spec.format = SDL_AUDIO_S16SYS;
_this->spec.channels = 1; device->spec.channels = 1;
/*_this->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/ //device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
/* Update the fragment size as size in bytes */ // Update the fragment size as size in bytes
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
LOGI("Try to open %u hz %u bit chan %u %s samples %u", LOGI("Try to open %u hz %u bit chan %u %s samples %u",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format), device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples); 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.locatorType = SL_DATALOCATOR_IODEVICE;
loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT; loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
@ -268,93 +267,93 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
audioSrc.pLocator = &loc_dev; audioSrc.pLocator = &loc_dev;
audioSrc.pFormat = NULL; audioSrc.pFormat = NULL;
/* configure audio sink */ // configure audio sink
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
loc_bufq.numBuffers = NUM_BUFFERS; loc_bufq.numBuffers = NUM_BUFFERS;
format_pcm.formatType = SL_DATAFORMAT_PCM; format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = _this->spec.channels; format_pcm.numChannels = device->spec.channels;
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */ format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format); format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format); format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER; format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
audioSnk.pLocator = &loc_bufq; audioSnk.pLocator = &loc_bufq;
audioSnk.pFormat = &format_pcm; audioSnk.pFormat = &format_pcm;
/* create audio recorder */ // create audio recorder
/* (requires the RECORD_AUDIO permission) */ // (requires the RECORD_AUDIO permission)
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req); result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioRecorder failed: %d", result); LOGE("CreateAudioRecorder failed: %d", result);
goto failed; goto failed;
} }
/* realize the recorder */ // realize the recorder
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE); result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed: %d", result); LOGE("RealizeAudioPlayer failed: %d", result);
goto failed; goto failed;
} }
/* get the record interface */ // get the record interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord); result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_RECORD interface get failed: %d", result); LOGE("SL_IID_RECORD interface get failed: %d", result);
goto failed; goto failed;
} }
/* get the buffer queue interface */ // get the buffer queue interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue); result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result); LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
goto failed; goto failed;
} }
/* register callback on the buffer queue */ // register callback on the buffer queue
/* context is '(SDL_PrivateAudioData *)_this->hidden' */ // context is '(SDL_PrivateAudioData *)device->hidden'
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, _this->hidden); result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, device->hidden);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed: %d", result); LOGE("RegisterCallback failed: %d", result);
goto failed; goto failed;
} }
/* Create the audio buffer semaphore */ // Create the audio buffer semaphore
audiodata->playsem = SDL_CreateSemaphore(0); audiodata->playsem = SDL_CreateSemaphore(0);
if (!audiodata->playsem) { if (!audiodata->playsem) {
LOGE("cannot create Semaphore!"); LOGE("cannot create Semaphore!");
goto failed; goto failed;
} }
/* Create the sound buffers */ // Create the sound buffers
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size); audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
if (audiodata->mixbuff == NULL) { if (audiodata->mixbuff == NULL) {
LOGE("mixbuffer allocate - out of memory"); LOGE("mixbuffer allocate - out of memory");
goto failed; goto failed;
} }
for (i = 0; i < NUM_BUFFERS; i++) { 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); result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Record set state failed: %d", result); LOGE("Record set state failed: %d", result);
goto failed; 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++) { 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) { if (SL_RESULT_SUCCESS != result) {
LOGE("Record enqueue buffers failed: %d", result); LOGE("Record enqueue buffers failed: %d", result);
goto failed; goto failed;
} }
} }
/* start recording */ // start recording
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING); result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Record set state failed: %d", result); LOGE("Record set state failed: %d", result);
@ -367,7 +366,7 @@ failed:
return SDL_SetError("Open device 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) static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context; struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
@ -376,20 +375,19 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
SDL_PostSemaphore(audiodata->playsem); SDL_PostSemaphore(audiodata->playsem);
} }
static void openslES_DestroyPCMPlayer(SDL_AudioDevice *_this) static void openslES_DestroyPCMPlayer(SDL_AudioDevice *device)
{ {
struct SDL_PrivateAudioData *audiodata = _this->hidden; struct SDL_PrivateAudioData *audiodata = device->hidden;
SLresult result;
/* set the player's state to 'stopped' */ // set the player's state to 'stopped'
if (bqPlayerPlay != NULL) { if (bqPlayerPlay != NULL) {
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED); const SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SetPlayState stopped failed: %d", 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) { if (bqPlayerObject != NULL) {
(*bqPlayerObject)->Destroy(bqPlayerObject); (*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) /* If we want to add floating point audio support (requires API level 21)
it can be done as described here: it can be done as described here:
https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
*/ */
if (SDL_GetAndroidSDKVersion() >= 21) { 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; SDL_AudioFormat test_format;
while ((test_format = *(closefmts++)) != 0) { while ((test_format = *(closefmts++)) != 0) {
if (SDL_AUDIO_ISSIGNED(test_format)) { if (SDL_AUDIO_ISSIGNED(test_format)) {
@ -436,40 +422,42 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
} }
if (!test_format) { 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"); LOGI("No compatible audio format, using signed 16-bit audio");
test_format = SDL_AUDIO_S16SYS; test_format = SDL_AUDIO_S16SYS;
} }
_this->spec.format = test_format; device->spec.format = test_format;
} else { } else {
/* Just go with signed 16-bit audio as it's the most compatible */ // Just go with signed 16-bit audio as it's the most compatible
_this->spec.format = SDL_AUDIO_S16SYS; device->spec.format = SDL_AUDIO_S16SYS;
} }
/* Update the fragment size as size in bytes */ // Update the fragment size as size in bytes
SDL_CalculateAudioSpec(&_this->spec); SDL_UpdatedAudioDeviceFormat(device);
LOGI("Try to open %u hz %s %u bit chan %u %s samples %u", 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), device->spec.freq, SDL_AUDIO_ISFLOAT(device->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(device->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples); 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.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
loc_bufq.numBuffers = NUM_BUFFERS; loc_bufq.numBuffers = NUM_BUFFERS;
SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM; format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = _this->spec.channels; format_pcm.numChannels = device->spec.channels;
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */ format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format); format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->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; format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
} else { } else {
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
} }
switch (_this->spec.channels) { switch (device->spec.channels) {
case 1: case 1:
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT; format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
break; break;
@ -495,14 +483,19 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1; format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1;
break; break;
default: default:
/* Unknown number of channels, fall back to stereo */ // Unknown number of channels, fall back to stereo
_this->spec.channels = 2; device->spec.channels = 2;
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
break; break;
} }
if (SDL_AUDIO_ISFLOAT(_this->spec.format)) { SLDataSink audioSnk;
/* Copy all setup into PCM EX structure */ 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.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
format_pcm_ex.endianness = format_pcm.endianness; format_pcm_ex.endianness = format_pcm.endianness;
format_pcm_ex.channelMask = format_pcm.channelMask; 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.bitsPerSample = format_pcm.bitsPerSample;
format_pcm_ex.containerSize = format_pcm.containerSize; format_pcm_ex.containerSize = format_pcm.containerSize;
format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
audioSrc.pFormat = (void *)&format_pcm_ex;
} }
audioSrc.pLocator = &loc_bufq; 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.locatorType = SL_DATALOCATOR_OUTPUTMIX;
loc_outmix.outputMix = outputMixObject; loc_outmix.outputMix = outputMixObject;
audioSnk.pLocator = &loc_outmix; audioSnk.pLocator = &loc_outmix;
audioSnk.pFormat = NULL; 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); result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioPlayer failed: %d", result); LOGE("CreateAudioPlayer failed: %d", result);
goto failed; goto failed;
} }
/* realize the player */ // realize the player
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed: %d", result); LOGE("RealizeAudioPlayer failed: %d", result);
goto failed; goto failed;
} }
/* get the play interface */ // get the play interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_PLAY interface get failed: %d", result); LOGE("SL_IID_PLAY interface get failed: %d", result);
goto failed; goto failed;
} }
/* get the buffer queue interface */ // get the buffer queue interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result); LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
goto failed; goto failed;
} }
/* register callback on the buffer queue */ // register callback on the buffer queue
/* context is '(SDL_PrivateAudioData *)_this->hidden' */ // context is '(SDL_PrivateAudioData *)device->hidden'
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, _this->hidden); result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, device->hidden);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed: %d", result); LOGE("RegisterCallback failed: %d", result);
goto failed; goto failed;
} }
#if 0 #if 0
/* get the volume interface */ // get the volume interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_VOLUME interface get failed: %d", result); LOGE("SL_IID_VOLUME interface get failed: %d", result);
/* goto failed; */ // goto failed;
} }
#endif #endif
/* Create the audio buffer semaphore */ struct SDL_PrivateAudioData *audiodata = device->hidden;
// Create the audio buffer semaphore
audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1); audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1);
if (!audiodata->playsem) { if (!audiodata->playsem) {
LOGE("cannot create Semaphore!"); LOGE("cannot create Semaphore!");
goto failed; goto failed;
} }
/* Create the sound buffers */ // Create the sound buffers
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size); audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
if (audiodata->mixbuff == NULL) { if (audiodata->mixbuff == NULL) {
LOGE("mixbuffer allocate - out of memory"); LOGE("mixbuffer allocate - out of memory");
goto failed; goto failed;
} }
for (i = 0; i < NUM_BUFFERS; i++) { for (int i = 0; i < NUM_BUFFERS; i++) {
audiodata->pmixbuff[i] = audiodata->mixbuff + i * _this->spec.size; 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); result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Play set state failed: %d", result); LOGE("Play set state failed: %d", result);
@ -598,103 +597,98 @@ failed:
return -1; 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)); device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (_this->hidden == NULL) { if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
if (_this->iscapture) { if (device->iscapture) {
LOGI("openslES_OpenDevice() %s for capture", devname); LOGI("openslES_OpenDevice() for capture");
return openslES_CreatePCMRecorder(_this); return openslES_CreatePCMRecorder(device);
} else { } else {
int ret; int ret;
LOGI("openslES_OpenDevice() %s for playing", devname); LOGI("openslES_OpenDevice() for playing");
ret = openslES_CreatePCMPlayer(_this); ret = openslES_CreatePCMPlayer(device);
if (ret < 0) { if (ret < 0) {
/* Another attempt to open the device with a lower frequency */ // Another attempt to open the device with a lower frequency
if (_this->spec.freq > 48000) { if (device->spec.freq > 48000) {
openslES_DestroyPCMPlayer(_this); openslES_DestroyPCMPlayer(device);
_this->spec.freq = 48000; device->spec.freq = 48000;
ret = openslES_CreatePCMPlayer(_this); ret = openslES_CreatePCMPlayer(device);
} }
} }
if (ret == 0) { if (ret != 0) {
return 0;
} else {
return SDL_SetError("Open device failed!"); 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()"); LOGV("openslES_WaitDevice()");
/* Wait for an audio chunk to finish */ // Wait for an audio chunk to finish
SDL_WaitSemaphore(audiodata->playsem); 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; struct SDL_PrivateAudioData *audiodata = device->hidden;
SLresult result;
LOGV("======openslES_PlayDevice()======"); LOGV("======openslES_PlayDevice()======");
/* Queue it up */ // Queue it up
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size); const SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, buflen);
audiodata->next_buffer++; audiodata->next_buffer++;
if (audiodata->next_buffer >= NUM_BUFFERS) { if (audiodata->next_buffer >= NUM_BUFFERS) {
audiodata->next_buffer = 0; audiodata->next_buffer = 0;
} }
/* If Enqueue fails, callback won't be called. // If Enqueue fails, callback won't be called.
* Post the semaphore, not to run out of buffer */ // Post the semaphore, not to run out of buffer
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
SDL_PostSemaphore(audiodata->playsem); SDL_PostSemaphore(audiodata->playsem);
} }
} }
/*/ n playn sem */ /// n playn sem
/* getbuf 0 - 1 */ // getbuf 0 - 1
/* fill buff 0 - 1 */ // fill buff 0 - 1
/* play 0 - 0 1 */ // play 0 - 0 1
/* wait 1 0 0 */ // wait 1 0 0
/* getbuf 1 0 0 */ // getbuf 1 0 0
/* fill buff 1 0 0 */ // fill buff 1 0 0
/* play 0 0 0 */ // play 0 0 0
/* wait */ // wait
/* */ //
/* okay.. */ // 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()"); LOGV("openslES_GetDeviceBuf()");
return audiodata->pmixbuff[audiodata->next_buffer]; 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; struct SDL_PrivateAudioData *audiodata = device->hidden;
SLresult result;
/* Wait for new recorded data */ // Copy it to the output buffer
SDL_WaitSemaphore(audiodata->playsem); SDL_assert(buflen == device->buffer_size);
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
/* Copy it to the output buffer */ // Re-enqueue the buffer
SDL_assert(buflen == _this->spec.size); const SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_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);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Record enqueue buffers failed: %d", result); LOGE("Record enqueue buffers failed: %d", result);
return -1; return -1;
@ -705,22 +699,24 @@ static int openslES_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int
audiodata->next_buffer = 0; 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; */ // struct SDL_PrivateAudioData *audiodata = device->hidden;
if (device->hidden) {
if (device->iscapture) {
LOGI("openslES_CloseDevice() for capture");
openslES_DestroyPCMRecorder(device);
} else {
LOGI("openslES_CloseDevice() for playing");
openslES_DestroyPCMPlayer(device);
}
if (_this->iscapture) { SDL_free(device->hidden);
LOGI("openslES_CloseDevice() for capture"); device->hidden = NULL;
openslES_DestroyPCMRecorder(_this);
} else {
LOGI("openslES_CloseDevice() for playing");
openslES_DestroyPCMPlayer(_this);
} }
SDL_free(_this->hidden);
} }
static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl) 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"); LOGI("openslES_Init() - set pointers");
/* Set the function pointers */ // Set the function pointers
/* impl->DetectDevices = openslES_DetectDevices; */ // impl->DetectDevices = openslES_DetectDevices;
impl->ThreadInit = Android_AudioThreadInit;
impl->OpenDevice = openslES_OpenDevice; impl->OpenDevice = openslES_OpenDevice;
impl->WaitDevice = openslES_WaitDevice; impl->WaitDevice = openslES_WaitDevice;
impl->PlayDevice = openslES_PlayDevice; impl->PlayDevice = openslES_PlayDevice;
impl->GetDeviceBuf = openslES_GetDeviceBuf; impl->GetDeviceBuf = openslES_GetDeviceBuf;
impl->WaitCaptureDevice = openslES_WaitDevice;
impl->CaptureFromDevice = openslES_CaptureFromDevice; impl->CaptureFromDevice = openslES_CaptureFromDevice;
impl->CloseDevice = openslES_CloseDevice; impl->CloseDevice = openslES_CloseDevice;
impl->Deinitialize = openslES_DestroyEngine; impl->Deinitialize = openslES_DestroyEngine;
/* and the capabilities */ // and the capabilities
impl->HasCaptureSupport = SDL_TRUE; impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_TRUE; impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE; impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
LOGI("openslES_Init() - success"); LOGI("openslES_Init() - success");
/* this audio target is available. */ // this audio target is available.
return SDL_TRUE; return SDL_TRUE;
} }
@ -761,7 +759,7 @@ AudioBootStrap openslES_bootstrap = {
void openslES_ResumeDevices(void) void openslES_ResumeDevices(void)
{ {
if (bqPlayerPlay != NULL) { 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); SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("openslES_ResumeDevices failed: %d", result); LOGE("openslES_ResumeDevices failed: %d", result);
@ -772,7 +770,7 @@ void openslES_ResumeDevices(void)
void openslES_PauseDevices(void) void openslES_PauseDevices(void)
{ {
if (bqPlayerPlay != NULL) { 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); SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("openslES_PauseDevices failed: %d", 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

View file

@ -86,7 +86,10 @@ void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
if (pAvSetMmThreadCharacteristicsW) { if (pAvSetMmThreadCharacteristicsW) {
DWORD idx = 0; DWORD idx = 0;
device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); 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) void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)

View file

@ -334,6 +334,7 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *device)
void WASAPI_PlatformThreadInit(SDL_AudioDevice *device) void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
{ {
// !!! FIXME: set this thread to "Pro Audio" priority. // !!! 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) void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)

View file

@ -233,7 +233,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls); JNIEnv *env, jclass jcls);
JNIEXPORT void JNICALL 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); jint device_id);
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
@ -242,7 +242,7 @@ JNIEXPORT void JNICALL
static JNINativeMethod SDLAudioManager_tab[] = { static JNINativeMethod SDLAudioManager_tab[] = {
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) }, { "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) } { "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) }
}; };
@ -350,8 +350,8 @@ static jmethodID midSupportsRelativeMouse;
static jclass mAudioManagerClass; static jclass mAudioManagerClass;
/* method signatures */ /* method signatures */
static jmethodID midGetAudioOutputDevices; static jmethodID midRegisterAudioDeviceCallback;
static jmethodID midGetAudioInputDevices; static jmethodID midUnregisterAudioDeviceCallback;
static jmethodID midAudioOpen; static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer; static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioWriteShortBuffer; static jmethodID midAudioWriteShortBuffer;
@ -681,12 +681,12 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls)); mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
midGetAudioOutputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass, midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioOutputDevices", "registerAudioDeviceCallback",
"()[I"); "()V");
midGetAudioInputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass, midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioInputDevices", "unregisterAudioDeviceCallback",
"()[I"); "()V");
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass, midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioOpen", "(IIIII)[I"); "audioOpen", "(IIIII)[I");
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass, 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, midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioSetThreadPriority", "(ZI)V"); "audioSetThreadPriority", "(ZI)V");
if (!midGetAudioOutputDevices || !midGetAudioInputDevices || !midAudioOpen || if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioOpen ||
!midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer ||
!midAudioClose || !midAudioClose ||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer ||
@ -1002,19 +1002,14 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE); 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 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,
jint device_id) jstring name, jint device_id)
{ {
if (SDL_GetCurrentAudioDriver() != NULL) { SDL_assert(SDL_GetCurrentAudioDriver() != NULL); // should have been started by Android_StartAudioHotplug inside an audio driver.
char device_name[64]; const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL);
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id); SDL_AddAudioDevice(is_capture ? SDL_TRUE : SDL_FALSE, SDL_strdup(utf8name), NULL, (void *)((size_t)device_id));
SDL_Log("Adding device with name %s, capture %d", device_name, is_capture); (*env)->ReleaseStringUTFChars(env, name, utf8name);
SDL_AddAudioDevice(is_capture, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
}
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
@ -1022,8 +1017,8 @@ SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean i
jint device_id) jint device_id)
{ {
if (SDL_GetCurrentAudioDriver() != NULL) { if (SDL_GetCurrentAudioDriver() != NULL) {
SDL_Log("Removing device with handle %d, capture %d", device_id + 1, is_capture); SDL_Log("Removing device with handle %d, capture %d", device_id, is_capture);
SDL_RemoveAudioDevice(is_capture, (void *)((size_t)device_id + 1)); SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id)));
} }
} }
@ -1564,58 +1559,25 @@ static void *audioBufferPinned = NULL;
static int captureBufferFormat = 0; static int captureBufferFormat = 0;
static jobject captureBuffer = NULL; 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(); JNIEnv *env = Android_JNI_GetEnv();
jintArray result; // 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);
if (is_input) { *default_output = *default_capture = NULL; // !!! FIXME: how do you decide the default device id?
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioInputDevices);
} else {
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioOutputDevices);
}
*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]; JNIEnv *env = Android_JNI_GetEnv();
int outputs[100]; (void) (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback);
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));
}
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; int audioformat;
jobject jbufobj = NULL; jobject jbufobj = NULL;
jobject result; jobject result;
@ -1640,10 +1602,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
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");
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 { } 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");
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) { if (result == NULL) {
/* Error during audio initialization, error printed from Java */ /* 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; spec->format = SDL_AUDIO_F32;
break; break;
default: 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->channels = resultElements[2];
spec->samples = resultElements[3]; device->sample_frames = resultElements[3];
(*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT); (*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
(*env)->DeleteLocalRef(env, result); (*env)->DeleteLocalRef(env, result);
@ -1680,7 +1642,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
switch (audioformat) { switch (audioformat) {
case ENCODING_PCM_8BIT: 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) { if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal); jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal); (*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1688,7 +1650,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break; } break;
case ENCODING_PCM_16BIT: 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) { if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal); jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal); (*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1696,7 +1658,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break; } break;
case ENCODING_PCM_FLOAT: 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) { if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal); jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(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(); JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id); (*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 */ /* 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. */ /* If the parameter silent is truthy then SDL_SetError() will not be called. */
static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent) static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)

View file

@ -30,6 +30,8 @@ extern "C" {
#include <EGL/eglplatform.h> #include <EGL/eglplatform.h>
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include "../../audio/SDL_sysaudio.h"
/* Interface from the SDL library into the Android Java activity */ /* Interface from the SDL library into the Android Java activity */
extern void Android_JNI_SetActivityTitle(const char *title); extern void Android_JNI_SetActivityTitle(const char *title);
extern void Android_JNI_SetWindowStyle(SDL_bool fullscreen); 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); extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void);
/* Audio support */ /* Audio support */
extern void Android_DetectDevices(void); void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec); 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_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);
extern void Android_JNI_FlushCapturedAudio(void); extern void Android_JNI_FlushCapturedAudio(void);
extern void Android_JNI_CloseAudioDevice(const int iscapture); extern void Android_JNI_CloseAudioDevice(const int iscapture);
extern void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id);
/* Detecting device type */ /* Detecting device type */
extern SDL_bool Android_IsDeXMode(void); extern SDL_bool Android_IsDeXMode(void);