android: Reworked audio backends for SDL3 audio API.

This involved moving an `#ifdef` out of SDL_audio.c for thread priority,
so the default ThreadInit now does the usual stuff for non-Android platforms,
the Android platforms provide an implementatin of ThreadInit with their
side of the `#ifdef` and other platforms that implement ThreadInit
incorporated the appropriate code...which is why WASAPI is touched in here.

The Android bits compile, but have not been tested, and there was some
reworkings in the Java bits, so this might need some further fixes still.
main
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 Context mContext;
private static final int[] NO_DEVICES = {};
private static AudioDeviceCallback mAudioDeviceCallback;
public static void initialize() {
@ -36,7 +34,7 @@ public class SDLAudioManager {
@Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
for (AudioDeviceInfo deviceInfo : addedDevices) {
addAudioDevice(deviceInfo.isSink(), deviceInfo.getId());
addAudioDevice(deviceInfo.isSink(), deviceInfo.getProductName().toString(), deviceInfo.getId());
}
}
@ -52,13 +50,10 @@ public class SDLAudioManager {
public static void setContext(Context context) {
mContext = context;
if (context != null) {
registerAudioDeviceCallback();
}
}
public static void release(Context context) {
unregisterAudioDeviceCallback(context);
// no-op atm
}
// Audio
@ -311,65 +306,20 @@ public class SDLAudioManager {
return null;
}
private static void registerAudioDeviceCallback() {
public static void registerAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
}
}
private static void unregisterAudioDeviceCallback(Context context) {
public static void unregisterAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
}
}
private static int[] ArrayListToArray(ArrayList<Integer> integers)
{
int[] ret = new int[integers.size()];
for (int i=0; i < ret.length; i++) {
ret[i] = integers.get(i).intValue();
}
return ret;
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioOutputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
ArrayList<Integer> arrlist = new ArrayList<Integer>();
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
/* Device cannot be opened */
if (dev.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
continue;
}
arrlist.add(dev.getId());
}
return ArrayListToArray(arrlist);
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioInputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
ArrayList<Integer> arrlist = new ArrayList<Integer>();
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
arrlist.add(dev.getId());
}
return ArrayListToArray(arrlist);
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
@ -535,6 +485,6 @@ public class SDLAudioManager {
public static native void removeAudioDevice(boolean isCapture, int deviceId);
public static native void addAudioDevice(boolean isCapture, int deviceId);
public static native void addAudioDevice(boolean isCapture, String name, int deviceId);
}

View File

@ -25,8 +25,6 @@
#include "../thread/SDL_systhread.h"
#include "../SDL_utils_c.h"
extern void Android_JNI_AudioSetThreadPriority(int, int); // we need this on Android in the audio device threads.
// Available audio drivers
static const AudioBootStrap *const bootstrap[] = {
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO
@ -412,7 +410,6 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
// stubs for audio drivers that don't need a specific entry point...
static void SDL_AudioThreadInit_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { /* no-op. */ }
@ -422,6 +419,11 @@ static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */
static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ }
static void SDL_AudioFreeDeviceHandle_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioThreadInit_Default(SDL_AudioDevice *device)
{
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
}
static void SDL_AudioDetectDevices_Default(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{
// you have to write your own implementation if these assertions fail.
@ -679,15 +681,6 @@ void SDL_AudioThreadFinalize(SDL_AudioDevice *device)
void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device)
{
SDL_assert(!device->iscapture);
// The audio mixing is always a high priority thread
#ifdef SDL_AUDIO_DRIVER_ANDROID
Android_JNI_AudioSetThreadPriority(SDL_FALSE, device->id);
#else
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
#endif
// Perform any thread setup
current_audio.impl.ThreadInit(device);
}
@ -781,14 +774,6 @@ static int SDLCALL OutputAudioThread(void *devicep) // thread entry point
void SDL_CaptureAudioThreadSetup(SDL_AudioDevice *device)
{
SDL_assert(device->iscapture);
// Audio capture is always a high priority thread (!!! FIXME: _should_ it be?)
#ifdef SDL_AUDIO_DRIVER_ANDROID
Android_JNI_AudioSetThreadPriority(SDL_TRUE, device->id);
#else
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
#endif
current_audio.impl.ThreadInit(device);
}
@ -1068,7 +1053,6 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
if ((devid == 0) && is_default) {
return SDL_SetError("No default audio device available");
return 0;
}
SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid);

View File

@ -34,16 +34,13 @@ struct SDL_PrivateAudioData
{
AAudioStream *stream;
/* Raw mixing buffer */
Uint8 *mixbuf;
int mixlen;
Uint8 *mixbuf; // Raw mixing buffer
int frame_size;
/* Resume device if it was paused automatically */
int resume;
int resume; // Resume device if it was paused automatically
};
/* Debug */
// Debug
#if 0
#define LOGI(...) SDL_Log(__VA_ARGS__);
#else
@ -52,7 +49,6 @@ struct SDL_PrivateAudioData
typedef struct AAUDIO_Data
{
AAudioStreamBuilder *builder;
void *handle;
#define SDL_PROC(ret, func, params) ret (*func) params;
#include "SDL_aaudiofuncs.h"
@ -79,11 +75,14 @@ static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_re
#define LIB_AAUDIO_SO "libaaudio.so"
static int AAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *private;
SDL_bool iscapture = _this->iscapture;
struct SDL_PrivateAudioData *hidden;
const SDL_bool iscapture = device->iscapture;
aaudio_result_t res;
SDL_assert(device->handle != NULL); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero.
LOGI(__func__);
if (iscapture) {
@ -93,75 +92,91 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
}
}
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
if (_this->hidden == NULL) {
hidden = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
if (hidden == NULL) {
return SDL_OutOfMemory();
}
private = _this->hidden;
ctx.AAudioStreamBuilder_setSampleRate(ctx.builder, _this->spec.freq);
ctx.AAudioStreamBuilder_setChannelCount(ctx.builder, _this->spec.channels);
if(devname != NULL) {
int aaudio_device_id = SDL_atoi(devname);
LOGI("Opening device id %d", aaudio_device_id);
ctx.AAudioStreamBuilder_setDeviceId(ctx.builder, aaudio_device_id);
}
{
aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
ctx.AAudioStreamBuilder_setDirection(ctx.builder, direction);
}
{
aaudio_format_t format = AAUDIO_FORMAT_PCM_FLOAT;
if (_this->spec.format == SDL_AUDIO_S16SYS) {
format = AAUDIO_FORMAT_PCM_I16;
} else if (_this->spec.format == SDL_AUDIO_S16SYS) {
format = AAUDIO_FORMAT_PCM_FLOAT;
}
ctx.AAudioStreamBuilder_setFormat(ctx.builder, format);
AAudioStreamBuilder *builder = NULL;
res = ctx.AAudio_createStreamBuilder(&builder);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res);
} else if (builder == NULL) {
LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL");
}
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",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream);
ctx.AAudioStreamBuilder_delete(builder);
res = ctx.AAudioStreamBuilder_openStream(ctx.builder, &private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
_this->spec.freq = ctx.AAudioStream_getSampleRate(private->stream);
_this->spec.channels = ctx.AAudioStream_getChannelCount(private->stream);
{
aaudio_format_t fmt = ctx.AAudioStream_getFormat(private->stream);
if (fmt == AAUDIO_FORMAT_PCM_I16) {
_this->spec.format = SDL_AUDIO_S16SYS;
} else if (fmt == AAUDIO_FORMAT_PCM_FLOAT) {
_this->spec.format = SDL_AUDIO_F32SYS;
}
device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream);
device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream);
format = ctx.AAudioStream_getFormat(hidden->stream);
if (format == AAUDIO_FORMAT_PCM_I16) {
device->spec.format = SDL_AUDIO_S16SYS;
} else if (format == AAUDIO_FORMAT_PCM_I32) {
device->spec.format = SDL_AUDIO_S32SYS;
} else if (format == AAUDIO_FORMAT_PCM_FLOAT) {
device->spec.format = SDL_AUDIO_F32SYS;
} else {
return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format);
}
device->sample_frames = ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 2;
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
device->spec.channels, SDL_AUDIO_ISBIGENDIAN(device->spec.format) ? "BE" : "LE", device->sample_frames);
SDL_CalculateAudioSpec(&_this->spec);
SDL_UpdatedAudioDeviceFormat(device);
/* Allocate mixing buffer */
// Allocate mixing buffer
if (!iscapture) {
private->mixlen = _this->spec.size;
private->mixbuf = (Uint8 *)SDL_malloc(private->mixlen);
if (private->mixbuf == NULL) {
hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
if (hidden->mixbuf == NULL) {
return SDL_OutOfMemory();
}
SDL_memset(private->mixbuf, _this->spec.silence, _this->spec.size);
SDL_memset(hidden->mixbuf, device->silence_value, device->buffer_size);
}
private->frame_size = _this->spec.channels * (SDL_AUDIO_BITSIZE(_this->spec.format) / 8);
hidden->frame_size = device->spec.channels * (SDL_AUDIO_BITSIZE(device->spec.format) / 8);
res = ctx.AAudioStream_requestStart(private->stream);
res = ctx.AAudioStream_requestStart(hidden->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture);
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
@ -171,55 +186,51 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
return 0;
}
static void AAUDIO_CloseDevice(SDL_AudioDevice *_this)
static void AAUDIO_CloseDevice(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *private = _this->hidden;
aaudio_result_t res;
LOGI(__func__);
struct SDL_PrivateAudioData *hidden = device->hidden;
if (hidden) {
LOGI(__func__);
if (private->stream) {
res = ctx.AAudioStream_requestStop(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStop %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return;
if (hidden->stream) {
ctx.AAudioStream_requestStop(hidden->stream);
ctx.AAudioStream_close(hidden->stream);
}
res = ctx.AAudioStream_close(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStreamBuilder_delete %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return;
}
SDL_free(hidden->mixbuf);
SDL_free(hidden);
device->hidden = NULL;
}
SDL_free(_this->hidden->mixbuf);
SDL_free(_this->hidden);
}
static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
{
struct SDL_PrivateAudioData *private = _this->hidden;
return private->mixbuf;
return device->hidden->mixbuf;
}
static void AAUDIO_PlayDevice(SDL_AudioDevice *_this)
static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
{
struct SDL_PrivateAudioData *private = _this->hidden;
aaudio_result_t res;
int64_t timeoutNanoseconds = 1 * 1000 * 1000; /* 8 ms */
res = ctx.AAudioStream_write(private->stream, private->mixbuf, private->mixlen / private->frame_size, timeoutNanoseconds);
AAudioStream *stream = device->hidden->stream;
while (!SDL_AtomicGet(&device->shutdown) && ((int) ctx.AAudioStream_getBufferSizeInFrames(stream)) < device->sample_frames) {
SDL_Delay(1);
}
}
static void AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
AAudioStream *stream = device->hidden->stream;
const aaudio_result_t res = ctx.AAudioStream_write(stream, buffer, device->sample_frames, 0);
if (res < 0) {
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
} else {
LOGI("SDL AAudio play: %d frames, wanted:%d frames", (int)res, private->mixlen / private->frame_size);
LOGI("SDL AAudio play: %d frames, wanted:%d frames", (int)res, sample_frames);
}
#if 0
/* Log under-run count */
// Log under-run count
{
static int prev = 0;
int32_t cnt = ctx.AAudioStream_getXRunCount(private->stream);
int32_t cnt = ctx.AAudioStream_getXRunCount(hidden->stream);
if (cnt != prev) {
SDL_Log("AAudio underrun: %d - total: %d", cnt - prev, cnt);
prev = cnt;
@ -228,41 +239,31 @@ static void AAUDIO_PlayDevice(SDL_AudioDevice *_this)
#endif
}
static int AAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
static int AAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
struct SDL_PrivateAudioData *private = _this->hidden;
aaudio_result_t res;
int64_t timeoutNanoseconds = 8 * 1000 * 1000; /* 8 ms */
res = ctx.AAudioStream_read(private->stream, buffer, buflen / private->frame_size, timeoutNanoseconds);
const aaudio_result_t res = ctx.AAudioStream_read(device->hidden->stream, buffer, device->sample_frames, 0);
if (res < 0) {
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
return -1;
}
LOGI("SDL AAudio capture:%d frames, wanted:%d frames", (int)res, buflen / private->frame_size);
return res * private->frame_size;
LOGI("SDL AAudio capture:%d frames, wanted:%d frames", (int)res, buflen / device->hidden->frame_size);
return res * device->hidden->frame_size;
}
static void AAUDIO_Deinitialize(void)
{
Android_StopAudioHotplug();
LOGI(__func__);
if (ctx.handle) {
if (ctx.builder) {
aaudio_result_t res;
res = ctx.AAudioStreamBuilder_delete(ctx.builder);
if (res != AAUDIO_OK) {
SDL_SetError("Failed AAudioStreamBuilder_delete %s", ctx.AAudio_convertResultToText(res));
}
}
SDL_UnloadObject(ctx.handle);
}
ctx.handle = NULL;
ctx.builder = NULL;
SDL_zero(ctx);
LOGI("End AAUDIO %s", SDL_GetError());
}
static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
{
aaudio_result_t res;
LOGI(__func__);
/* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release,
@ -279,189 +280,96 @@ static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO);
if (ctx.handle == NULL) {
LOGI("SDL couldn't find " LIB_AAUDIO_SO);
goto failure;
return SDL_FALSE;
}
if (AAUDIO_LoadFunctions(&ctx) < 0) {
goto failure;
SDL_UnloadObject(ctx.handle);
SDL_zero(ctx);
return SDL_FALSE;
}
res = ctx.AAudio_createStreamBuilder(&ctx.builder);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
goto failure;
}
if (ctx.builder == NULL) {
LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
goto failure;
}
impl->DetectDevices = Android_DetectDevices;
impl->ThreadInit = Android_AudioThreadInit;
impl->DetectDevices = Android_StartAudioHotplug;
impl->Deinitialize = AAUDIO_Deinitialize;
impl->OpenDevice = AAUDIO_OpenDevice;
impl->CloseDevice = AAUDIO_CloseDevice;
impl->WaitDevice = AAUDIO_WaitDevice;
impl->PlayDevice = AAUDIO_PlayDevice;
impl->GetDeviceBuf = AAUDIO_GetDeviceBuf;
impl->WaitCaptureDevice = AAUDIO_WaitDevice;
impl->CaptureFromDevice = AAUDIO_CaptureFromDevice;
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
/* and the capabilities */
impl->HasCaptureSupport = SDL_TRUE;
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
/* this audio target is available. */
LOGI("SDL AAUDIO_Init OK");
return SDL_TRUE;
failure:
if (ctx.handle) {
if (ctx.builder) {
ctx.AAudioStreamBuilder_delete(ctx.builder);
}
SDL_UnloadObject(ctx.handle);
}
ctx.handle = NULL;
ctx.builder = NULL;
return SDL_FALSE;
}
AudioBootStrap AAUDIO_bootstrap = {
"AAudio", "AAudio audio driver", AAUDIO_Init, SDL_FALSE
};
/* Pause (block) all non already paused audio devices by taking their mixer lock */
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)
{
int i;
/* AAUDIO driver is not used */
if (ctx.handle == NULL) {
return;
}
for (i = 0; i < get_max_num_audio_dev(); i++) {
SDL_AudioDevice *_this = get_audio_dev(i);
SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (_this == NULL) {
continue;
}
if (_this->iscapture) {
captureDevice = _this;
} else {
audioDevice = _this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = (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;
}
}
if (ctx.handle != NULL) { // AAUDIO driver is used?
(void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL);
}
}
/* 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)
{
int i;
/* AAUDIO driver is not used */
if (ctx.handle == NULL) {
return;
}
for (i = 0; i < get_max_num_audio_dev(); i++) {
SDL_AudioDevice *_this = get_audio_dev(i);
SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (_this == NULL) {
continue;
}
if (_this->iscapture) {
captureDevice = _this;
} else {
audioDevice = _this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
if (private->resume) {
SDL_AtomicSet(&audioDevice->paused, 0);
private->resume = SDL_FALSE;
SDL_UnlockMutex(audioDevice->mixer_lock);
}
if (private->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
}
if (captureDevice != NULL && captureDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
if (private->resume) {
SDL_AtomicSet(&captureDevice->paused, 0);
private->resume = SDL_FALSE;
SDL_UnlockMutex(captureDevice->mixer_lock);
}
if (private->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
if (res != AAUDIO_OK) {
LOGI("SDL Failed AAudioStream_requestStart %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
}
}
if (ctx.handle != NULL) { // AAUDIO driver is used?
(void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL);
}
}
@ -470,50 +378,29 @@ void AAUDIO_ResumeDevices(void)
None of the standard state queries indicate any problem in my testing. And the error callback doesn't actually get called.
But, AAudioStream_getTimestamp() does return AAUDIO_ERROR_INVALID_STATE
*/
SDL_bool AAUDIO_DetectBrokenPlayState(void)
static SDL_bool DetectBrokenPlayStatePerDevice(SDL_AudioDevice *device, void *userdata)
{
int i;
/* AAUDIO driver is not used */
if (ctx.handle == NULL) {
return SDL_FALSE;
}
for (i = 0; i < get_max_num_audio_dev(); i++) {
SDL_AudioDevice *_this = get_audio_dev(i);
SDL_AudioDevice *audioDevice = NULL;
SDL_AudioDevice *captureDevice = NULL;
if (_this == NULL) {
continue;
}
if (_this->iscapture) {
captureDevice = _this;
} else {
audioDevice = _this;
}
if (audioDevice != NULL && audioDevice->hidden != NULL) {
struct SDL_PrivateAudioData *private = audioDevice->hidden;
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;
}
SDL_assert(device != NULL);
if (!device->iscapture && device->hidden != NULL) {
struct SDL_PrivateAudioData *hidden = device->hidden;
int64_t framePosition, timeNanoseconds;
aaudio_result_t res = ctx.AAudioStream_getTimestamp(hidden->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds);
if (res == AAUDIO_ERROR_INVALID_STATE) {
aaudio_stream_state_t currentState = ctx.AAudioStream_getState(hidden->stream);
// AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing.
if (currentState == AAUDIO_STREAM_STATE_STARTED) {
LOGI("SDL AAUDIO_DetectBrokenPlayState: detected invalid audio device state: AAudioStream_getTimestamp result=%d, framePosition=%lld, timeNanoseconds=%lld, getState=%d", (int)res, (long long)framePosition, (long long)timeNanoseconds, (int)currentState);
return SDL_TRUE; // this guy.
}
}
(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_write, (AAudioStream * stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_setBufferSizeInFrames, (AAudioStream * stream, int32_t numFrames))
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerBurst, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream))
SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream))

View File

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

View File

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

View File

@ -86,7 +86,10 @@ void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
if (pAvSetMmThreadCharacteristicsW) {
DWORD idx = 0;
device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx);
} else {
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
}
}
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)

View File

@ -334,6 +334,7 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *device)
void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
{
// !!! FIXME: set this thread to "Pro Audio" priority.
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
}
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)

View File

@ -233,7 +233,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture, jstring name,
jint device_id);
JNIEXPORT void JNICALL
@ -242,7 +242,7 @@ JNIEXPORT void JNICALL
static JNINativeMethod SDLAudioManager_tab[] = {
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) },
{ "addAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
{ "addAudioDevice", "(ZLjava/lang/String;I)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
{ "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) }
};
@ -350,8 +350,8 @@ static jmethodID midSupportsRelativeMouse;
static jclass mAudioManagerClass;
/* method signatures */
static jmethodID midGetAudioOutputDevices;
static jmethodID midGetAudioInputDevices;
static jmethodID midRegisterAudioDeviceCallback;
static jmethodID midUnregisterAudioDeviceCallback;
static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer;
static jmethodID midAudioWriteShortBuffer;
@ -681,12 +681,12 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
midGetAudioOutputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioOutputDevices",
"()[I");
midGetAudioInputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"getAudioInputDevices",
"()[I");
midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"registerAudioDeviceCallback",
"()V");
midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"unregisterAudioDeviceCallback",
"()V");
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioOpen", "(IIIII)[I");
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
@ -710,7 +710,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
"audioSetThreadPriority", "(ZI)V");
if (!midGetAudioOutputDevices || !midGetAudioInputDevices || !midAudioOpen ||
if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioOpen ||
!midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer ||
!midAudioClose ||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer ||
@ -1002,19 +1002,14 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE);
}
extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle);
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle);
JNIEXPORT void JNICALL
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
jint device_id)
jstring name, jint device_id)
{
if (SDL_GetCurrentAudioDriver() != NULL) {
char device_name[64];
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
SDL_Log("Adding device with name %s, capture %d", device_name, is_capture);
SDL_AddAudioDevice(is_capture, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
}
SDL_assert(SDL_GetCurrentAudioDriver() != NULL); // should have been started by Android_StartAudioHotplug inside an audio driver.
const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL);
SDL_AddAudioDevice(is_capture ? SDL_TRUE : SDL_FALSE, SDL_strdup(utf8name), NULL, (void *)((size_t)device_id));
(*env)->ReleaseStringUTFChars(env, name, utf8name);
}
JNIEXPORT void JNICALL
@ -1022,8 +1017,8 @@ SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean i
jint device_id)
{
if (SDL_GetCurrentAudioDriver() != NULL) {
SDL_Log("Removing device with handle %d, capture %d", device_id + 1, is_capture);
SDL_RemoveAudioDevice(is_capture, (void *)((size_t)device_id + 1));
SDL_Log("Removing device with handle %d, capture %d", device_id, is_capture);
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id)));
}
}
@ -1564,58 +1559,25 @@ static void *audioBufferPinned = NULL;
static int captureBufferFormat = 0;
static jobject captureBuffer = NULL;
static void Android_JNI_GetAudioDevices(int *devices, int *length, int max_len, int is_input)
void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
{
JNIEnv *env = Android_JNI_GetEnv();
jintArray result;
if (is_input) {
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioInputDevices);
} else {
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioOutputDevices);
}
*length = (*env)->GetArrayLength(env, result);
*length = SDL_min(*length, max_len);
(*env)->GetIntArrayRegion(env, result, 0, *length, devices);
// this will fire the callback for each existing device right away (which will eventually SDL_AddAudioDevice), and again later when things change.
(void) (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midRegisterAudioDeviceCallback);
*default_output = *default_capture = NULL; // !!! FIXME: how do you decide the default device id?
}
void Android_DetectDevices(void)
void Android_StopAudioHotplug(void)
{
int inputs[100];
int outputs[100];
int inputs_length = 0;
int outputs_length = 0;
SDL_zeroa(inputs);
Android_JNI_GetAudioDevices(inputs, &inputs_length, 100, 1 /* input devices */);
for (int i = 0; i < inputs_length; ++i) {
int device_id = inputs[i];
char device_name[64];
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
SDL_Log("Adding input device with name %s", device_name);
SDL_AddAudioDevice(SDL_TRUE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
}
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));
}
JNIEnv *env = Android_JNI_GetEnv();
(void) (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback);
}
int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec)
int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device)
{
const SDL_bool iscapture = device->iscapture;
SDL_AudioSpec *spec = &device->spec;
const int device_id = (int) ((size_t) device->handle);
int audioformat;
jobject jbufobj = NULL;
jobject result;
@ -1640,10 +1602,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
if (iscapture) {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
} else {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
}
if (result == NULL) {
/* Error during audio initialization, error printed from Java */
@ -1668,10 +1630,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
spec->format = SDL_AUDIO_F32;
break;
default:
return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
return SDL_SetError("Unexpected audio format from Java: %d", audioformat);
}
spec->channels = resultElements[2];
spec->samples = resultElements[3];
device->sample_frames = resultElements[3];
(*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
(*env)->DeleteLocalRef(env, result);
@ -1680,7 +1642,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
switch (audioformat) {
case ENCODING_PCM_8BIT:
{
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels);
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1688,7 +1650,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break;
case ENCODING_PCM_16BIT:
{
jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels);
jshortArray audioBufferLocal = (*env)->NewShortArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1696,7 +1658,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
} break;
case ENCODING_PCM_FLOAT:
{
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels);
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, device->sample_frames * spec->channels);
if (audioBufferLocal) {
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
(*env)->DeleteLocalRef(env, audioBufferLocal);
@ -1887,12 +1849,17 @@ void Android_JNI_CloseAudioDevice(const int iscapture)
}
}
void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
static void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
{
JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id);
}
void Android_AudioThreadInit(SDL_AudioDevice *device)
{
Android_JNI_AudioSetThreadPriority((int) device->iscapture, (int)device->instance_id);
}
/* Test for an exception and call SDL_SetError with its detail if one occurs */
/* If the parameter silent is truthy then SDL_SetError() will not be called. */
static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)

View File

@ -30,6 +30,8 @@ extern "C" {
#include <EGL/eglplatform.h>
#include <android/native_window_jni.h>
#include "../../audio/SDL_sysaudio.h"
/* Interface from the SDL library into the Android Java activity */
extern void Android_JNI_SetActivityTitle(const char *title);
extern void Android_JNI_SetWindowStyle(SDL_bool fullscreen);
@ -47,14 +49,15 @@ extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void);
extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void);
/* Audio support */
extern void Android_DetectDevices(void);
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec);
void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
void Android_StopAudioHotplug(void);
extern void Android_AudioThreadInit(SDL_AudioDevice *device);
extern int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device);
extern void *Android_JNI_GetAudioBuffer(void);
extern void Android_JNI_WriteAudioBuffer(void);
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
extern void Android_JNI_FlushCapturedAudio(void);
extern void Android_JNI_CloseAudioDevice(const int iscapture);
extern void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id);
/* Detecting device type */
extern SDL_bool Android_IsDeXMode(void);