summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Miso Kim <meeso.kim@gmail.com>2017-08-23 12:09:38 +0900
committer Vas Crabb <cuavas@users.noreply.github.com>2017-08-23 13:09:38 +1000
commit41e044ae012c6b33ea6c9c88860be787b4b5d09e (patch)
tree15776d71fe5bc13044c7ded734450589d534096f
parent1de4735438cf385ec99ea51b74c2274ddeee2616 (diff)
Modification for Android build (NDK r14b, build-tools 21.1.2 and gradle 2.2.1) (#2585)
-rw-r--r--3rdparty/SDL2/src/audio/android/SDL_androidaudio.c44
-rw-r--r--3rdparty/SDL2/src/audio/android/opensl_io.c557
-rw-r--r--3rdparty/SDL2/src/audio/android/opensl_io.h129
-rw-r--r--android-project/app/build.gradle8
-rw-r--r--android-project/app/src/main/AndroidManifest.xml2
-rw-r--r--android-project/app/src/main/assets/mame.ini201
-rw-r--r--android-project/app/src/main/assets/ui.ini60
-rw-r--r--android-project/app/src/main/java/org/libsdl/app/SDLActivity.java198
-rw-r--r--android-project/app/src/main/java/org/mamedev/mame/MAME.java77
-rw-r--r--android-project/settings.gradle2
-rw-r--r--makefile2
-rw-r--r--scripts/genie.lua1
-rw-r--r--scripts/src/3rdparty.lua8
-rw-r--r--scripts/toolchain.lua8
-rw-r--r--src/osd/sdl/sdlmain.cpp3
15 files changed, 1237 insertions, 63 deletions
diff --git a/3rdparty/SDL2/src/audio/android/SDL_androidaudio.c b/3rdparty/SDL2/src/audio/android/SDL_androidaudio.c
index 96f6d631ad6..0e60fb0c6f2 100644
--- a/3rdparty/SDL2/src/audio/android/SDL_androidaudio.c
+++ b/3rdparty/SDL2/src/audio/android/SDL_androidaudio.c
@@ -31,10 +31,13 @@
#include "../../core/android/SDL_android.h"
+#include "opensl_io.h"
+
#include <android/log.h>
static SDL_AudioDevice* audioDevice = NULL;
static SDL_AudioDevice* captureDevice = NULL;
+static OPENSL_STREAM *sl = NULL;
static int
ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
@@ -57,7 +60,8 @@ ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
test_format = SDL_FirstAudioFormat(this->spec.format);
while (test_format != 0) { /* no "UNKNOWN" constant */
- if ((test_format == AUDIO_U8) || (test_format == AUDIO_S16LSB)) {
+// if ((test_format == AUDIO_U8) || (test_format == AUDIO_S16LSB)) {
+ if (test_format == AUDIO_S16MSB) {
this->spec.format = test_format;
break;
}
@@ -75,20 +79,32 @@ ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
this->spec.channels = 1;
}
+ #if 0
if (this->spec.freq < 8000) {
this->spec.freq = 8000;
}
if (this->spec.freq > 48000) {
this->spec.freq = 48000;
}
-
+ #endif
/* TODO: pass in/return a (Java) device ID */
- this->spec.samples = Android_JNI_OpenAudioDevice(iscapture, this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
-
- if (this->spec.samples == 0) {
- /* Init failed? */
- return SDL_SetError("Java-side initialization failed!");
- }
+ //this->spec.samples = Android_JNI_OpenAudioDevice(iscapture, this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
+ if (!sl & !iscapture)
+ {
+ if (this->spec.size != 0)
+ {
+// this->spec.samples = this->spec.size / this->spec.channels / 2;
+ } else {
+ this->spec.samples = 400;
+ this->spec.size = this->spec.channels * this->spec.samples;
+ }
+ sl = android_OpenAudioDevice(this->spec.freq, this->spec.channels, this->spec.channels, this->spec.samples);
+ __android_log_print(ANDROID_LOG_INFO, "SDL", "android_OpenAudioDevice: %x, freq=%d, channels=%d, %d samples", (int)sl, this->spec.freq, this->spec.channels, this->spec.samples);
+ if (!sl) {
+ /* Init failed? */
+ return SDL_SetError("Java-side initialization failed!");
+ }
+ }
SDL_CalculateAudioSpec(&this->spec);
@@ -98,13 +114,17 @@ ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
static void
ANDROIDAUDIO_PlayDevice(_THIS)
{
- Android_JNI_WriteAudioBuffer();
+ //Android_JNI_WriteAudioBuffer();
+ android_AudioOut2(sl);
}
static Uint8 *
ANDROIDAUDIO_GetDeviceBuf(_THIS)
{
- return Android_JNI_GetAudioBuffer();
+ //return Android_JNI_GetAudioBuffer();
+ Uint8 *buf = android_GetDeviceBuffer(sl);
+// __android_log_print(ANDROID_LOG_INFO, "SDL", "GetDeviceBuf: %x", (int) buf);
+ return buf;
}
static int
@@ -148,9 +168,9 @@ ANDROIDAUDIO_Init(SDL_AudioDriverImpl * impl)
impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
/* and the capabilities */
- impl->HasCaptureSupport = SDL_TRUE;
+ impl->HasCaptureSupport = SDL_FALSE;
impl->OnlyHasDefaultOutputDevice = 1;
- impl->OnlyHasDefaultCaptureDevice = 1;
+ impl->OnlyHasDefaultCaptureDevice = 0;
return 1; /* this audio target is available. */
}
diff --git a/3rdparty/SDL2/src/audio/android/opensl_io.c b/3rdparty/SDL2/src/audio/android/opensl_io.c
new file mode 100644
index 00000000000..5e295706b57
--- /dev/null
+++ b/3rdparty/SDL2/src/audio/android/opensl_io.c
@@ -0,0 +1,557 @@
+/*
+opensl_io.c:
+Android OpenSL input/output module
+Copyright (c) 2012, Victor Lazzarini
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <limits.h>
+#include "opensl_io.h"
+#define CONV16BIT 32768
+#define CONVMYFLT (1./32768.)
+
+#include <android/log.h>
+#define LOG_TAG "SDL_android"
+
+static void* createThreadLock(void);
+static int waitThreadLock(void *lock);
+static void notifyThreadLock(void *lock);
+static void destroyThreadLock(void *lock);
+static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
+static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
+
+// creates the OpenSL ES audio engine
+static SLresult openSLCreateEngine(OPENSL_STREAM *p)
+{
+ SLresult result;
+ // create engine
+ result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL);
+ if(result != SL_RESULT_SUCCESS) goto engine_end;
+
+ // realize the engine
+ result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE);
+ if(result != SL_RESULT_SUCCESS) goto engine_end;
+
+ // get the engine interface, which is needed in order to create other objects
+ result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine));
+ if(result != SL_RESULT_SUCCESS) goto engine_end;
+
+ engine_end:
+ return result;
+}
+
+// opens the OpenSL ES device for output
+static SLresult openSLPlayOpen(OPENSL_STREAM *p)
+{
+ SLresult result;
+ SLuint32 sr = p->sr;
+ SLuint32 channels = p->outchannels;
+
+ if(channels){
+ // configure audio source
+ SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
+
+ switch(sr){
+
+ case 8000:
+ sr = SL_SAMPLINGRATE_8;
+ break;
+ case 11025:
+ sr = SL_SAMPLINGRATE_11_025;
+ break;
+ case 16000:
+ sr = SL_SAMPLINGRATE_16;
+ break;
+ case 22050:
+ sr = SL_SAMPLINGRATE_22_05;
+ break;
+ case 24000:
+ sr = SL_SAMPLINGRATE_24;
+ break;
+ case 32000:
+ sr = SL_SAMPLINGRATE_32;
+ break;
+ case 44100:
+ sr = SL_SAMPLINGRATE_44_1;
+ break;
+ case 48000:
+ sr = SL_SAMPLINGRATE_48;
+ break;
+ case 64000:
+ sr = SL_SAMPLINGRATE_64;
+ break;
+ case 88200:
+ sr = SL_SAMPLINGRATE_88_2;
+ break;
+ case 96000:
+ sr = SL_SAMPLINGRATE_96;
+ break;
+ case 192000:
+ sr = SL_SAMPLINGRATE_192;
+ break;
+ default:
+ return -1;
+ }
+
+ const SLInterfaceID ids[] = {SL_IID_VOLUME};
+ const SLboolean req[] = {SL_BOOLEAN_FALSE};
+ result = (*p->engineEngine)->CreateOutputMix(p->engineEngine, &(p->outputMixObject), 1, ids, req);
+ if(result != SL_RESULT_SUCCESS) goto end_openaudio;
+
+ // realize the output mix
+ result = (*p->outputMixObject)->Realize(p->outputMixObject, SL_BOOLEAN_FALSE);
+
+ int speakers;
+ if(channels > 1)
+ speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+ else speakers = SL_SPEAKER_FRONT_CENTER;
+ SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,channels, sr,
+ SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
+ speakers, SL_BYTEORDER_LITTLEENDIAN};
+
+ SLDataSource audioSrc = {&loc_bufq, &format_pcm};
+
+ // configure audio sink
+ SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, p->outputMixObject};
+ SLDataSink audioSnk = {&loc_outmix, NULL};
+
+ // create audio player
+ const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
+ const SLboolean req1[] = {SL_BOOLEAN_TRUE};
+ result = (*p->engineEngine)->CreateAudioPlayer(p->engineEngine, &(p->bqPlayerObject), &audioSrc, &audioSnk,
+ 1, ids1, req1);
+ if(result != SL_RESULT_SUCCESS) goto end_openaudio;
+
+ // realize the player
+ result = (*p->bqPlayerObject)->Realize(p->bqPlayerObject, SL_BOOLEAN_FALSE);
+ if(result != SL_RESULT_SUCCESS) goto end_openaudio;
+
+ // get the play interface
+ result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_PLAY, &(p->bqPlayerPlay));
+ if(result != SL_RESULT_SUCCESS) goto end_openaudio;
+
+ // get the buffer queue interface
+ result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &(p->bqPlayerBufferQueue));
+ if(result != SL_RESULT_SUCCESS) goto end_openaudio;
+
+ // register callback on the buffer queue
+ result = (*p->bqPlayerBufferQueue)->RegisterCallback(p->bqPlayerBufferQueue, bqPlayerCallback, p);
+ if(result != SL_RESULT_SUCCESS) goto end_openaudio;
+
+ // set the player's state to playing
+ result = (*p->bqPlayerPlay)->SetPlayState(p->bqPlayerPlay, SL_PLAYSTATE_PLAYING);
+
+ end_openaudio:
+ return result;
+ }
+ return SL_RESULT_SUCCESS;
+}
+
+// Open the OpenSL ES device for input
+static SLresult openSLRecOpen(OPENSL_STREAM *p){
+
+ SLresult result;
+ SLuint32 sr = p->sr;
+ SLuint32 channels = p->inchannels;
+
+ if(channels){
+
+ switch(sr){
+
+ case 8000:
+ sr = SL_SAMPLINGRATE_8;
+ break;
+ case 11025:
+ sr = SL_SAMPLINGRATE_11_025;
+ break;
+ case 16000:
+ sr = SL_SAMPLINGRATE_16;
+ break;
+ case 22050:
+ sr = SL_SAMPLINGRATE_22_05;
+ break;
+ case 24000:
+ sr = SL_SAMPLINGRATE_24;
+ break;
+ case 32000:
+ sr = SL_SAMPLINGRATE_32;
+ break;
+ case 44100:
+ sr = SL_SAMPLINGRATE_44_1;
+ break;
+ case 48000:
+ sr = SL_SAMPLINGRATE_48;
+ break;
+ case 64000:
+ sr = SL_SAMPLINGRATE_64;
+ break;
+ case 88200:
+ sr = SL_SAMPLINGRATE_88_2;
+ break;
+ case 96000:
+ sr = SL_SAMPLINGRATE_96;
+ break;
+ case 192000:
+ sr = SL_SAMPLINGRATE_192;
+ break;
+ default:
+ return -1;
+ }
+
+ // configure audio source
+ SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
+ SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
+ SLDataSource audioSrc = {&loc_dev, NULL};
+
+ // configure audio sink
+ int speakers;
+ if(channels > 1)
+ speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+ else speakers = SL_SPEAKER_FRONT_CENTER;
+ SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
+ SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, sr,
+ SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
+ speakers, SL_BYTEORDER_LITTLEENDIAN};
+ SLDataSink audioSnk = {&loc_bq, &format_pcm};
+
+ // create audio recorder
+ // (requires the RECORD_AUDIO permission)
+ const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
+ const SLboolean req[1] = {SL_BOOLEAN_TRUE};
+ result = (*p->engineEngine)->CreateAudioRecorder(p->engineEngine, &(p->recorderObject), &audioSrc,
+ &audioSnk, 1, id, req);
+ if (SL_RESULT_SUCCESS != result) goto end_recopen;
+
+ // realize the audio recorder
+ result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE);
+ if (SL_RESULT_SUCCESS != result) goto end_recopen;
+
+ // get the record interface
+ result = (*p->recorderObject)->GetInterface(p->recorderObject, SL_IID_RECORD, &(p->recorderRecord));
+ if (SL_RESULT_SUCCESS != result) goto end_recopen;
+
+ // get the buffer queue interface
+ result = (*p->recorderObject)->GetInterface(p->recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &(p->recorderBufferQueue));
+ if (SL_RESULT_SUCCESS != result) goto end_recopen;
+
+ // register callback on the buffer queue
+ result = (*p->recorderBufferQueue)->RegisterCallback(p->recorderBufferQueue, bqRecorderCallback,
+ p);
+ if (SL_RESULT_SUCCESS != result) goto end_recopen;
+ result = (*p->recorderRecord)->SetRecordState(p->recorderRecord, SL_RECORDSTATE_RECORDING);
+
+ end_recopen:
+ return result;
+ }
+ else return SL_RESULT_SUCCESS;
+
+
+}
+
+// close the OpenSL IO and destroy the audio engine
+static void openSLDestroyEngine(OPENSL_STREAM *p){
+
+ // destroy buffer queue audio player object, and invalidate all associated interfaces
+ if (p->bqPlayerObject != NULL) {
+ (*p->bqPlayerObject)->Destroy(p->bqPlayerObject);
+ p->bqPlayerObject = NULL;
+ p->bqPlayerPlay = NULL;
+ p->bqPlayerBufferQueue = NULL;
+ p->bqPlayerEffectSend = NULL;
+ }
+
+ // destroy audio recorder object, and invalidate all associated interfaces
+ if (p->recorderObject != NULL) {
+ (*p->recorderObject)->Destroy(p->recorderObject);
+ p->recorderObject = NULL;
+ p->recorderRecord = NULL;
+ p->recorderBufferQueue = NULL;
+ }
+
+ // destroy output mix object, and invalidate all associated interfaces
+ if (p->outputMixObject != NULL) {
+ (*p->outputMixObject)->Destroy(p->outputMixObject);
+ p->outputMixObject = NULL;
+ }
+
+ // destroy engine object, and invalidate all associated interfaces
+ if (p->engineObject != NULL) {
+ (*p->engineObject)->Destroy(p->engineObject);
+ p->engineObject = NULL;
+ p->engineEngine = NULL;
+ }
+
+}
+
+
+// open the android audio device for input and/or output
+OPENSL_STREAM *android_OpenAudioDevice(int sr, int inchannels, int outchannels, int bufferframes){
+
+ OPENSL_STREAM *p;
+ p = (OPENSL_STREAM *) calloc(sizeof(OPENSL_STREAM),1);
+
+ p->inchannels = inchannels;
+ p->outchannels = outchannels;
+ p->sr = sr;
+ p->inlock = createThreadLock();
+ p->outlock = createThreadLock();
+
+ if((p->outBufSamples = bufferframes*outchannels) != 0) {
+ if((p->outputBuffer[0] = (short *) calloc(p->outBufSamples, sizeof(short))) == NULL ||
+ (p->outputBuffer[1] = (short *) calloc(p->outBufSamples, sizeof(short))) == NULL) {
+ android_CloseAudioDevice(p);
+ return NULL;
+ }
+ __android_log_print(ANDROID_LOG_INFO, "OPENSL", "android_OpenAudioDevice: outBufSamples(%d), %x, %x", p->outBufSamples, (int) p->outputBuffer[0], (int) p->outputBuffer[1]);
+ }
+
+ if((p->inBufSamples = bufferframes*inchannels) != 0){
+ if((p->inputBuffer[0] = (short *) calloc(p->inBufSamples, sizeof(short))) == NULL ||
+ (p->inputBuffer[1] = (short *) calloc(p->inBufSamples, sizeof(short))) == NULL){
+ android_CloseAudioDevice(p);
+ return NULL;
+ }
+ __android_log_print(ANDROID_LOG_INFO, "OPENSL", "android_OpenAudioDevice: inBufSamples(%d)", p->inBufSamples);
+ }
+
+ p->currentInputIndex = 0;
+ p->currentOutputBuffer = 0;
+ p->currentInputIndex = p->inBufSamples;
+ p->currentInputBuffer = 0;
+
+ if(openSLCreateEngine(p) != SL_RESULT_SUCCESS) {
+ android_CloseAudioDevice(p);
+ return NULL;
+ }
+// __android_log_print(ANDROID_LOG_INFO, "OPENSL", "android_OpenAudioDevice: openSLCreateEngine");
+#if 1
+ if(openSLRecOpen(p) != SL_RESULT_SUCCESS) {
+// android_CloseAudioDevice(p);
+// return NULL;
+ }
+// __android_log_print(ANDROID_LOG_INFO, "OPENSL", "android_OpenAudioDevice: openSLRecOpen");
+#endif
+ if(openSLPlayOpen(p) != SL_RESULT_SUCCESS) {
+ android_CloseAudioDevice(p);
+ return NULL;
+ }
+// __android_log_print(ANDROID_LOG_INFO, "OPENSL", "android_OpenAudioDevice: openSLPlayOpen");
+
+ notifyThreadLock(p->outlock);
+ notifyThreadLock(p->inlock);
+
+ p->time = 0.;
+ return p;
+}
+
+// close the android audio device
+void android_CloseAudioDevice(OPENSL_STREAM *p){
+
+ if (p == NULL)
+ return;
+
+ openSLDestroyEngine(p);
+
+ if (p->inlock != NULL) {
+ notifyThreadLock(p->inlock);
+ destroyThreadLock(p->inlock);
+ p->inlock = NULL;
+ }
+
+ if (p->outlock != NULL) {
+ notifyThreadLock(p->outlock);
+ destroyThreadLock(p->outlock);
+ p->inlock = NULL;
+ }
+
+ if (p->outputBuffer[0] != NULL) {
+ free(p->outputBuffer[0]);
+ p->outputBuffer[0] = NULL;
+ }
+
+ if (p->outputBuffer[1] != NULL) {
+ free(p->outputBuffer[1]);
+ p->outputBuffer[1] = NULL;
+ }
+
+ if (p->inputBuffer[0] != NULL) {
+ free(p->inputBuffer[0]);
+ p->inputBuffer[0] = NULL;
+ }
+
+ if (p->inputBuffer[1] != NULL) {
+ free(p->inputBuffer[1]);
+ p->inputBuffer[1] = NULL;
+ }
+
+ free(p);
+}
+
+// returns timestamp of the processed stream
+double android_GetTimestamp(OPENSL_STREAM *p){
+ return p->time;
+}
+
+
+// this callback handler is called every time a buffer finishes recording
+void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
+{
+ OPENSL_STREAM *p = (OPENSL_STREAM *) context;
+ notifyThreadLock(p->inlock);
+}
+
+// gets a buffer of size samples from the device
+int android_AudioIn(OPENSL_STREAM *p,float *buffer,int size){
+ short *inBuffer;
+ int i, bufsamps = p->inBufSamples, index = p->currentInputIndex;
+ if(p == NULL || bufsamps == 0) return 0;
+
+ inBuffer = p->inputBuffer[p->currentInputBuffer];
+ for(i=0; i < size; i++){
+ if (index >= bufsamps) {
+ waitThreadLock(p->inlock);
+ (*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
+ inBuffer,bufsamps*sizeof(short));
+ p->currentInputBuffer = (p->currentInputBuffer ? 0 : 1);
+ index = 0;
+ inBuffer = p->inputBuffer[p->currentInputBuffer];
+ }
+ buffer[i] = (float) inBuffer[index++]*CONVMYFLT;
+ }
+ p->currentInputIndex = index;
+ if(p->outchannels == 0) p->time += (double) size/(p->sr*p->inchannels);
+ return i;
+}
+
+// this callback handler is called every time a buffer finishes playing
+void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
+{
+ OPENSL_STREAM *p = (OPENSL_STREAM *) context;
+ notifyThreadLock(p->outlock);
+}
+
+unsigned char *android_GetDeviceBuffer(OPENSL_STREAM *p)
+{
+ p->currentOutputBuffer = (p->currentOutputBuffer ? 0 : 1);
+ waitThreadLock(p->outlock);
+// __android_log_print(ANDROID_LOG_INFO, "OPENSL", "android_GetDeviceBuffer: buf[%d]=%x", p->currentOutputBuffer, (int) p->outputBuffer[p->currentOutputBuffer]);
+ return (unsigned char *)p->outputBuffer[p->currentOutputBuffer];
+}
+
+void android_AudioOut2(OPENSL_STREAM *p)
+{
+ short *outBuffer = p->outputBuffer[p->currentOutputBuffer];
+ int bufsamps = p->outBufSamples;
+ (*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue, outBuffer, bufsamps*sizeof(short));
+}
+// puts a buffer of size samples to the device
+int android_AudioOut(OPENSL_STREAM *p, float *buffer, int size){
+
+ short *outBuffer;
+ int i, bufsamps = p->outBufSamples, index = p->currentOutputIndex;
+ if(p == NULL || bufsamps == 0) return 0;
+ outBuffer = p->outputBuffer[p->currentOutputBuffer];
+
+ for (i=0; i < size; i++) {
+
+ // clean conversion from float to short int
+ float x = buffer[i];
+ if (x>1.0) { x=1.0; } else if (x<-1.0) { x=-1.0; };
+ outBuffer[index++] = (short)( x * 32767.0 );
+
+ if (index >= p->outBufSamples) {
+ waitThreadLock(p->outlock);
+ (*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue,
+ outBuffer,bufsamps*sizeof(short));
+ p->currentOutputBuffer = (p->currentOutputBuffer ? 0 : 1);
+ index = 0;
+ outBuffer = p->outputBuffer[p->currentOutputBuffer];
+ }
+ }
+ p->currentOutputIndex = index;
+ p->time += (double) size/(p->sr*p->outchannels);
+ return i;
+}
+
+//----------------------------------------------------------------------
+// thread Locks
+// to ensure synchronisation between callbacks and processing code
+void* createThreadLock(void)
+{
+ threadLock *p;
+ p = (threadLock*) malloc(sizeof(threadLock));
+ if (p == NULL)
+ return NULL;
+ memset(p, 0, sizeof(threadLock));
+ if (pthread_mutex_init(&(p->m), (pthread_mutexattr_t*) NULL) != 0) {
+ free((void*) p);
+ return NULL;
+ }
+ if (pthread_cond_init(&(p->c), (pthread_condattr_t*) NULL) != 0) {
+ pthread_mutex_destroy(&(p->m));
+ free((void*) p);
+ return NULL;
+ }
+ p->s = (unsigned char) 1;
+
+ return p;
+}
+
+int waitThreadLock(void *lock)
+{
+ threadLock *p;
+ p = (threadLock*) lock;
+ pthread_mutex_lock(&(p->m));
+ while (!p->s) {
+ pthread_cond_wait(&(p->c), &(p->m));
+ }
+ p->s = (unsigned char) 0;
+ pthread_mutex_unlock(&(p->m));
+ return 0;
+}
+
+void notifyThreadLock(void *lock)
+{
+ threadLock *p;
+ p = (threadLock*) lock;
+ pthread_mutex_lock(&(p->m));
+ p->s = (unsigned char) 1;
+ pthread_cond_signal(&(p->c));
+ pthread_mutex_unlock(&(p->m));
+}
+
+void destroyThreadLock(void *lock)
+{
+ threadLock *p;
+ p = (threadLock*) lock;
+ if (p == NULL)
+ return;
+ notifyThreadLock(p);
+ pthread_cond_destroy(&(p->c));
+ pthread_mutex_destroy(&(p->m));
+ free(p);
+} \ No newline at end of file
diff --git a/3rdparty/SDL2/src/audio/android/opensl_io.h b/3rdparty/SDL2/src/audio/android/opensl_io.h
new file mode 100644
index 00000000000..5c04cd2fdd7
--- /dev/null
+++ b/3rdparty/SDL2/src/audio/android/opensl_io.h
@@ -0,0 +1,129 @@
+/*
+opensl_io.c:
+Android OpenSL input/output module header
+Copyright (c) 2012, Victor Lazzarini
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef OPENSL_IO
+#define OPENSL_IO
+
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+typedef struct threadLock_{
+ pthread_mutex_t m;
+ pthread_cond_t c;
+ unsigned char s;
+} threadLock;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct opensl_stream {
+
+ // engine interfaces
+ SLObjectItf engineObject;
+ SLEngineItf engineEngine;
+
+ // output mix interfaces
+ SLObjectItf outputMixObject;
+
+ // buffer queue player interfaces
+ SLObjectItf bqPlayerObject;
+ SLPlayItf bqPlayerPlay;
+ SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
+ SLEffectSendItf bqPlayerEffectSend;
+
+ // recorder interfaces
+ SLObjectItf recorderObject;
+ SLRecordItf recorderRecord;
+ SLAndroidSimpleBufferQueueItf recorderBufferQueue;
+
+ // buffer indexes
+ int currentInputIndex;
+ int currentOutputIndex;
+
+ // current buffer half (0, 1)
+ int currentOutputBuffer;
+ int currentInputBuffer;
+
+ // buffers
+ short *outputBuffer[2];
+ short *inputBuffer[2];
+
+ // size of buffers
+ int outBufSamples;
+ int inBufSamples;
+
+ // locks
+ void* inlock;
+ void* outlock;
+
+ double time;
+ int inchannels;
+ int outchannels;
+ int sr;
+
+} OPENSL_STREAM;
+
+ /*
+ Open the audio device with a given sampling rate (sr), input and output channels and IO buffer size
+ in frames. Returns a handle to the OpenSL stream
+ */
+ OPENSL_STREAM* android_OpenAudioDevice(int sr, int inchannels, int outchannels, int bufferframes);
+ /*
+ Close the audio device
+ */
+ void android_CloseAudioDevice(OPENSL_STREAM *p);
+ /*
+ Read a buffer from the OpenSL stream *p, of size samples. Returns the number of samples read.
+ */
+ int android_AudioIn(OPENSL_STREAM *p, float *buffer,int size);
+ /*
+ Write a buffer to the OpenSL stream *p, of size samples. Returns the number of samples written.
+ */
+ int android_AudioOut(OPENSL_STREAM *p, float *buffer,int size);
+ /*
+ Get the current IO block time in seconds
+ */
+ double android_GetTimestamp(OPENSL_STREAM *p);
+ /*
+ Android SDL interface for playing
+ */
+ void android_AudioOut2(OPENSL_STREAM *p);
+ /*
+ Android SDL interface for getBuffer
+ */
+ unsigned char *android_GetDeviceBuffer(OPENSL_STREAM *p);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // #ifndef OPENSL_IO \ No newline at end of file
diff --git a/android-project/app/build.gradle b/android-project/app/build.gradle
index 4ee2ec059e7..35377908b88 100644
--- a/android-project/app/build.gradle
+++ b/android-project/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 18
- buildToolsVersion "23.0.3"
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
defaultConfig {
applicationId "org.mamedev.mame"
- minSdkVersion 18
- targetSdkVersion 18
+ minSdkVersion 21
+ targetSdkVersion 21
ndk {
moduleName 'main'
diff --git a/android-project/app/src/main/AndroidManifest.xml b/android-project/app/src/main/AndroidManifest.xml
index 087d63f2763..7b235a4b2fd 100644
--- a/android-project/app/src/main/AndroidManifest.xml
+++ b/android-project/app/src/main/AndroidManifest.xml
@@ -33,7 +33,7 @@
android:hardwareAccelerated="true" >
<activity android:name="MAME"
android:label="@string/app_name"
- android:configChanges="keyboardHidden|orientation"
+ android:configChanges="keyboardHidden|orientation|screenSize"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/android-project/app/src/main/assets/mame.ini b/android-project/app/src/main/assets/mame.ini
new file mode 100644
index 00000000000..73064edeece
--- /dev/null
+++ b/android-project/app/src/main/assets/mame.ini
@@ -0,0 +1,201 @@
+#
+# CORE CONFIGURATION OPTIONS
+#
+readconfig 1
+writeconfig 0
+
+#
+# CORE SEARCH PATH OPTIONS
+#
+homepath .
+rompath roms;/sdcard/Download;/sdcard/mame/roms
+hashpath hash
+samplepath samples
+artpath artwork
+ctrlrpath ctrlr
+inipath .
+fontpath .
+cheatpath cheat
+crosshairpath crosshair
+pluginspath plugins
+languagepath language
+swpath software
+
+#
+# CORE OUTPUT DIRECTORY OPTIONS
+#
+cfg_directory cfg
+nvram_directory nvram
+input_directory inp
+state_directory sta
+snapshot_directory snap
+diff_directory diff
+comment_directory comments
+
+#
+# CORE STATE/PLAYBACK OPTIONS
+#
+state
+autosave 1
+playback
+record
+record_timecode 0
+exit_after_playback 0
+mngwrite
+aviwrite
+wavwrite
+snapname %g/%i
+snapsize auto
+snapview internal
+snapbilinear 1
+statename %g
+burnin 0
+
+#
+# CORE PERFORMANCE OPTIONS
+#
+autoframeskip 1
+frameskip 8
+seconds_to_run 0
+throttle 1
+sleep 0
+speed 1.0
+refreshspeed 30
+
+#
+# CORE RENDER OPTIONS
+#
+keepaspect 1
+unevenstretch 1
+unevenstretchx 0
+unevenstretchy 0
+autostretchxy 0
+intoverscan 0
+intscalex 0
+intscaley 0
+
+#
+# CORE ROTATION OPTIONS
+#
+rotate 1
+ror 0
+rol 0
+autoror 0
+autorol 0
+flipx 0
+flipy 0
+
+#
+# CORE ARTWORK OPTIONS
+#
+artwork_crop 0
+use_backdrops 1
+use_overlays 1
+use_bezels 1
+use_cpanels 1
+use_marquees 1
+
+#
+# CORE SCREEN OPTIONS
+#
+brightness 1.0
+contrast 1.0
+gamma 1.0
+pause_brightness 0.65
+effect none
+
+#
+# CORE VECTOR OPTIONS
+#
+beam_width_min 1.0
+beam_width_max 1.0
+beam_intensity_weight 0
+flicker 0
+
+#
+# CORE SOUND OPTIONS
+#
+samplerate 48000
+samples 1
+volume 0
+
+#
+# CORE INPUT OPTIONS
+#
+coin_lockout 1
+ctrlr
+mouse 0
+joystick 1
+lightgun 0
+multikeyboard 0
+multimouse 0
+steadykey 0
+ui_active 1
+offscreen_reload 0
+joystick_map auto
+joystick_deadzone 0.3
+joystick_saturation 0.85
+natural 0
+joystick_contradictory 0
+coin_impulse 0
+
+#
+# CORE INPUT AUTOMATIC ENABLE OPTIONS
+#
+paddle_device keyboard
+adstick_device keyboard
+pedal_device keyboard
+dial_device keyboard
+trackball_device keyboard
+lightgun_device keyboard
+positional_device keyboard
+mouse_device mouse
+
+#
+# CORE DEBUGGING OPTIONS
+#
+verbose 0
+log 0
+oslog 0
+debug 0
+update_in_pause 0
+debugscript
+
+#
+# CORE COMM OPTIONS
+#
+comm_localhost 0.0.0.0
+comm_localport 15112
+comm_remotehost 127.0.0.1
+comm_remoteport 15112
+
+#
+# CORE MISC OPTIONS
+#
+drc 1
+drc_use_c 0
+drc_log_uml 0
+drc_log_native 0
+bios
+cheat 0
+skip_gameinfo 1
+uifont default
+ui cabinet
+ramsize
+confirm_quit 0
+ui_mouse 1
+autoboot_command
+autoboot_delay 0
+autoboot_script
+console 0
+plugins 1
+plugin
+noplugin
+language English
+
+#
+# HTTP SERVER OPTIONS
+#
+http 0
+http_port 8080
+http_root web
diff --git a/android-project/app/src/main/assets/ui.ini b/android-project/app/src/main/assets/ui.ini
new file mode 100644
index 00000000000..55f09f7897a
--- /dev/null
+++ b/android-project/app/src/main/assets/ui.ini
@@ -0,0 +1,60 @@
+#
+# UI SEARCH PATH OPTIONS
+#
+historypath history;dats;.
+extrainipath folders
+cabinets_directory cabinets;cabdevs
+cpanels_directory cpanel
+pcbs_directory pcb
+flyers_directory flyers
+titles_directory titles
+ends_directory ends
+marquees_directory marquees
+artwork_preview_directory "artwork preview;artpreview"
+bosses_directory bosses
+logos_directory logo
+scores_directory scores
+versus_directory versus
+gameover_directory gameover
+howto_directory howto
+select_directory select
+icons_directory icons
+covers_directory covers
+ui_path ui
+
+#
+# UI MISC OPTIONS
+#
+remember_last 1
+enlarge_snaps 1
+forced4x3 1
+use_background 1
+skip_biosmenu 1
+skip_partsmenu 1
+last_used_filter Available
+last_used_machine narc
+info_audit_enabled 0
+hide_romless 1
+
+#
+# UI OPTIONS
+#
+infos_text_size 1
+font_rows 30
+hide_main_panel 0
+ui_border_color ffffffff
+ui_bg_color ef101030
+ui_clone_color ff808080
+ui_dipsw_color ffffff00
+ui_gfxviewer_color ef101030
+ui_mousedown_bg_color b0606000
+ui_mousedown_color ffffff80
+ui_mouseover_bg_color 70404000
+ui_mouseover_color ffffff80
+ui_selected_bg_color ef808000
+ui_selected_color ffffff00
+ui_slider_color ffffffff
+ui_subitem_color ffffffff
+ui_text_bg_color ef000000
+ui_text_color ffffffff
+ui_unavail_color ff404040
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index 0a2e5d87e31..39db993d18a 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -17,7 +17,7 @@ import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.widget.AbsoluteLayout;
+import android.widget.RelativeLayout;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -29,6 +29,9 @@ import android.graphics.drawable.Drawable;
import android.media.*;
import android.hardware.*;
import android.content.pm.ActivityInfo;
+import java.io.*;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
/**
SDL Activity
@@ -59,6 +62,7 @@ public class SDLActivity extends Activity {
// Audio
protected static AudioTrack mAudioTrack;
+ protected static AudioRecord mAudioRecord;
/**
* This method is called by SDL before loading the native shared libraries.
@@ -106,13 +110,14 @@ public class SDLActivity extends Activity {
mJoystickHandler = null;
mSDLThread = null;
mAudioTrack = null;
+ mAudioRecord = null;
mExitCalledFromJava = false;
mBrokenLibraries = false;
mIsPaused = false;
mIsSurfaceReady = false;
mHasFocus = true;
}
-
+
// Setup
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -171,7 +176,7 @@ public class SDLActivity extends Activity {
mJoystickHandler = new SDLJoystickHandler();
}
- mLayout = new AbsoluteLayout(this);
+ mLayout = new RelativeLayout(this);
mLayout.addView(mSurface);
setContentView(mLayout);
@@ -186,6 +191,15 @@ public class SDLActivity extends Activity {
SDLActivity.onNativeDropFile(filename);
}
}
+
+ View decorView = getWindow().getDecorView();
+ // Hide both the navigation bar and the status bar.
+ // SYSTEM_UI_FLAG_FULLSCREEN is only available on Android 4.1 and higher, but as
+ // a general rule, you should design your app to hide the status bar whenever you
+ // hide the navigation bar.
+ int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ decorView.setSystemUiVisibility(uiOptions);
}
// Events
@@ -272,6 +286,11 @@ public class SDLActivity extends Activity {
// Reset everything in case the user re opens the app
SDLActivity.initialize();
}
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ }
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
@@ -367,10 +386,13 @@ public class SDLActivity extends Activity {
break;
case COMMAND_TEXTEDIT_HIDE:
if (mTextEdit != null) {
- mTextEdit.setVisibility(View.GONE);
+ // Note: On some devices setting view to GONE creates a flicker in landscape.
+ // Setting the View's sizes to 0 is similar to GONE but without the flicker.
+ // The sizes will be set to useful values when the keyboard is shown again.
+ mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
- InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
+ // InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ // imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
}
break;
case COMMAND_SET_KEEP_SCREEN_ON:
@@ -504,8 +526,9 @@ public class SDLActivity extends Activity {
@Override
public void run() {
- AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(
- w, h + HEIGHT_PADDING, x, y);
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
+ params.leftMargin = x;
+ params.topMargin = y;
if (mTextEdit == null) {
mTextEdit = new DummyEdit(getContext());
@@ -528,7 +551,8 @@ public class SDLActivity extends Activity {
*/
public static boolean showTextInput(int x, int y, int w, int h) {
// Transfer the task to the main thread as a Runnable
- return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
+// return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
+ return true;
}
/**
@@ -543,7 +567,7 @@ public class SDLActivity extends Activity {
/**
* This method is called by SDL using JNI.
*/
- public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+ public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
@@ -622,13 +646,72 @@ public class SDLActivity extends Activity {
/**
* This method is called by SDL using JNI.
*/
- public static void audioQuit() {
+ public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+ int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
+ int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+
+ Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+ // Let the user pick a larger buffer if they really want -- but ye
+ // gods they probably shouldn't, the minimums are horrifyingly high
+ // latency already
+ desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
+
+ if (mAudioRecord == null) {
+ mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
+ channelConfig, audioFormat, desiredFrames * frameSize);
+
+ // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
+ if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+ Log.e(TAG, "Failed during initialization of AudioRecord");
+ mAudioRecord.release();
+ mAudioRecord = null;
+ return -1;
+ }
+
+ mAudioRecord.startRecording();
+ }
+
+ Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+ return 0;
+ }
+
+ /** This method is called by SDL using JNI. */
+ public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
+ // !!! FIXME: this is available in API Level 23. Until then, we always block. :(
+ //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+ return mAudioRecord.read(buffer, 0, buffer.length);
+ }
+
+ /** This method is called by SDL using JNI. */
+ public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
+ // !!! FIXME: this is available in API Level 23. Until then, we always block. :(
+ //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+ return mAudioRecord.read(buffer, 0, buffer.length);
+ }
+
+
+ /** This method is called by SDL using JNI. */
+ public static void audioClose() {
if (mAudioTrack != null) {
mAudioTrack.stop();
+ mAudioTrack.release();
mAudioTrack = null;
}
}
+ /** This method is called by SDL using JNI. */
+ public static void captureClose() {
+ if (mAudioRecord != null) {
+ mAudioRecord.stop();
+ mAudioRecord.release();
+ mAudioRecord = null;
+ }
+ }
+
+
// Input
/**
@@ -662,6 +745,21 @@ public class SDLActivity extends Activity {
}
}
+ // Check if a given device is considered a possible SDL joystick
+ public static boolean isDeviceSDLJoystick(int deviceId) {
+ InputDevice device = InputDevice.getDevice(deviceId);
+ // We cannot use InputDevice.isVirtual before API 16, so let's accept
+ // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
+ if ((device == null) || (deviceId < 0)) {
+ return false;
+ }
+ int sources = device.getSources();
+ return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
+ ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
+ ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
+ );
+ }
+
// APK expansion files support
/** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
@@ -976,6 +1074,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// Keep track of the surface size to normalize touch events
protected static float mWidth, mHeight;
+ static float[] px, py;
// Startup
public SDLSurface(Context context) {
@@ -994,10 +1093,11 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
if(Build.VERSION.SDK_INT >= 12) {
setOnGenericMotionListener(new SDLGenericMotionListener_API12());
}
-
// Some arbitrary defaults to avoid a potential division by zero
mWidth = 1.0f;
mHeight = 1.0f;
+ px = new float[10];
+ py = new float[10];
}
public void handlePause() {
@@ -1014,6 +1114,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
}
public Surface getNativeSurface() {
+ Log.v("SDL", "getNativeSurface()");
return getHolder().getSurface();
}
@@ -1033,7 +1134,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
SDLActivity.mIsSurfaceReady = false;
SDLActivity.onNativeSurfaceDestroyed();
}
-
+
// Called when the surface is resized
@Override
public void surfaceChanged(SurfaceHolder holder,
@@ -1088,8 +1189,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
mWidth = width;
mHeight = height;
SDLActivity.onNativeResize(width, height, sdlFormat, mDisplay.getRefreshRate());
- Log.v("SDL", "Window size: " + width + "x" + height);
-
+ //Log.v("SDL", "Window size: " + width + "x" + height);
boolean skip = false;
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
@@ -1137,13 +1237,13 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
final Thread sdlThread = new Thread(new SDLMain(), "SDLThread");
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
- sdlThread.start();
// Set up a listener thread to catch when the native thread ends
SDLActivity.mSDLThread = new Thread(new Runnable(){
@Override
public void run(){
try {
+ sdlThread.start();
sdlThread.join();
}
catch(Exception e){}
@@ -1167,11 +1267,14 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// Dispatch the different events depending on where they come from
- // Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
- // So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as KEYBOARD
-
- if ( (event.getSource() & InputDevice.SOURCE_GAMEPAD) != 0 ||
- (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) {
+ // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
+ // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
+ //
+ // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
+ // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
+ // So, retrieve the device itself and check all of its sources
+ if (SDLActivity.isDeviceSDLJoystick(event.getDeviceId())) {
+ // Note that we process events with specific key codes here
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
return true;
@@ -1183,7 +1286,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
}
}
- if( (event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
+ if ((event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
//Log.v("SDL", "key down: " + keyCode);
SDLActivity.onNativeKeyDown(keyCode);
@@ -1196,6 +1299,20 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
}
}
+ if ((event.getSource() & InputDevice.SOURCE_MOUSE) != 0) {
+ // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
+ // they are ignored here because sending them as mouse input to SDL is messy
+ if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
+ switch (event.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ case KeyEvent.ACTION_UP:
+ // mark the event as handled or it will be handled by system
+ // handling KEYCODE_BACK by system will call onBackPressed()
+ return true;
+ }
+ }
+ }
+
return false;
}
@@ -1214,7 +1331,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// !!! FIXME: dump this SDK check after 2.0.4 ships and require API14.
if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) {
if (Build.VERSION.SDK_INT < 14) {
- mouseButton = 1; // For Android==12 all mouse buttons are the left button
+ mouseButton = 1; // all mouse buttons are the left button
} else {
try {
mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
@@ -1236,12 +1353,16 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// see the documentation of getPressure(i)
p = 1.0f;
}
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+// SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+ SDLActivity.onNativeMouse(1, action, event.getX(i) - px[i], event.getY(i) - py[i]);
+ px[i] = event.getX(i);
+ py[i] = event.getY(i);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
+ //SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_ENTER);
// Primary pointer up/down, the index is always zero
i = 0;
case MotionEvent.ACTION_POINTER_UP:
@@ -1260,7 +1381,8 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// see the documentation of getPressure(i)
p = 1.0f;
}
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+ SDLActivity.onNativeMouse(1, action, 0, 0);
+ //SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
@@ -1274,7 +1396,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
// see the documentation of getPressure(i)
p = 1.0f;
}
- SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
+ //SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
@@ -1328,7 +1450,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
}
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
y / SensorManager.GRAVITY_EARTH,
- event.values[2] / SensorManager.GRAVITY_EARTH - 1);
+ event.values[2] / SensorManager.GRAVITY_EARTH);
}
}
}
@@ -1355,7 +1477,7 @@ class DummyEdit extends View implements View.OnKeyListener {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// This handles the hardware keyboard input
- if (event.isPrintingKey()) {
+ if (event.isPrintingKey() || keyCode == KeyEvent.KEYCODE_SPACE) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
}
@@ -1368,8 +1490,7 @@ class DummyEdit extends View implements View.OnKeyListener {
} else if (event.getAction() == KeyEvent.ACTION_UP) {
SDLActivity.onNativeKeyUp(keyCode);
return true;
- }
-
+ }
return false;
}
@@ -1418,7 +1539,7 @@ class SDLInputConnection extends BaseInputConnection {
*/
int keyCode = event.getKeyCode();
if (event.getAction() == KeyEvent.ACTION_DOWN) {
- if (event.isPrintingKey()) {
+ if (event.isPrintingKey() || keyCode == KeyEvent.KEYCODE_SPACE) {
commitText(String.valueOf((char) event.getUnicodeChar()), 1);
}
SDLActivity.onNativeKeyDown(keyCode);
@@ -1519,13 +1640,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
if (joystick == null) {
joystick = new SDLJoystick();
InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
-
- if (
- (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0
- ||
- (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_BUTTON) != 0
- )
- {
+ if (SDLActivity.isDeviceSDLJoystick(deviceIds[i])) {
joystick.device_id = deviceIds[i];
joystick.name = joystickDevice.getName();
joystick.axes = new ArrayList<InputDevice.MotionRange>();
@@ -1534,7 +1649,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges ) {
- if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) {
+ if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
@@ -1588,7 +1703,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
@Override
public boolean handleMotionEvent(MotionEvent event) {
- if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
+ if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
switch(action) {
@@ -1627,8 +1742,7 @@ class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_DPAD:
- SDLActivity.handleJoystickMotionEvent(event);
- return true;
+ return SDLActivity.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE:
action = event.getActionMasked();
diff --git a/android-project/app/src/main/java/org/mamedev/mame/MAME.java b/android-project/app/src/main/java/org/mamedev/mame/MAME.java
index d243b59159f..d54ab834a1e 100644
--- a/android-project/app/src/main/java/org/mamedev/mame/MAME.java
+++ b/android-project/app/src/main/java/org/mamedev/mame/MAME.java
@@ -1,9 +1,84 @@
package org.mamedev.mame;
+import java.io.*;
+import android.app.*;
+import android.os.*;
+import android.content.res.AssetManager;
+import android.util.Log;
import org.libsdl.app.SDLActivity;
+import android.view.*;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
/**
SDL Activity
*/
public class MAME extends SDLActivity {
+ private static final String TAG = "MAME";
+ // Setup
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ copyAssetAll("mame.ini");
+ copyAssetAll("ui.ini");
+ copyAssetAll("roms");
+ }
+
+ public void copyAssetAll(String srcPath) {
+ AssetManager assetMgr = this.getAssets();
+ String assets[] = null;
+ try {
+ String destPath = getExternalFilesDir(null) + File.separator + srcPath;
+ assets = assetMgr.list(srcPath);
+ if (assets.length == 0) {
+ copyFile(srcPath, destPath);
+ } else {
+ File dir = new File(destPath);
+ if (!dir.exists())
+ dir.mkdir();
+ for (String element : assets) {
+ copyAssetAll(srcPath + File.separator + element);
+ }
+ }
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ public void copyFile(String srcFile, String destFile) {
+ AssetManager assetMgr = this.getAssets();
+
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ is = assetMgr.open(srcFile);
+ if (new File(destFile).exists() == false)
+ {
+ os = new FileOutputStream(destFile);
+
+ byte[] buffer = new byte[1024];
+ int read;
+ while ((read = is.read(buffer)) != -1) {
+ os.write(buffer, 0, read);
+ }
+ is.close();
+ os.flush();
+ os.close();
+ Log.v(TAG, "copy from Asset:" + destFile);
+ }
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
-}
+ int keyCode = event.getKeyCode();
+ // Ignore certain special keys so they're handled by Android
+ if (((event.getSource() & InputDevice.SOURCE_CLASS_BUTTON) != 0) && (keyCode == KeyEvent.KEYCODE_BACK)) {
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ return super.dispatchKeyEvent(event);
+ }
+ }
diff --git a/android-project/settings.gradle b/android-project/settings.gradle
index e7b4def49cb..e66d0475ed0 100644
--- a/android-project/settings.gradle
+++ b/android-project/settings.gradle
@@ -1 +1,3 @@
+gradle.ext.sdkVersion = 21
+
include ':app'
diff --git a/makefile b/makefile
index 4bfaf317ee8..5cd780d7dd7 100644
--- a/makefile
+++ b/makefile
@@ -1533,7 +1533,7 @@ generate: \
$(GENDIR)/includes/SDL2:
-$(call MKDIR,$@)
- -$(call COPY,3rdparty/SDL2/include/,$(GENDIR)/includes/SDL2)
+ -$(call COPY,3rdparty/SDL2/include,$(GENDIR)/includes/SDL2)
ifneq ($(NEW_GIT_VERSION),$(OLD_GIT_VERSION))
stale:
diff --git a/scripts/genie.lua b/scripts/genie.lua
index 27c6819d1b5..22d7d05f69c 100644
--- a/scripts/genie.lua
+++ b/scripts/genie.lua
@@ -1051,6 +1051,7 @@ configuration { "android*" }
"-Wno-undef",
"-Wno-typedef-redefinition",
"-Wno-unknown-warning-option",
+ "-Wno-incompatible-ms-struct",
}
buildoptions_cpp {
"-x c++",
diff --git a/scripts/src/3rdparty.lua b/scripts/src/3rdparty.lua
index 703eb242f00..38961dd045e 100644
--- a/scripts/src/3rdparty.lua
+++ b/scripts/src/3rdparty.lua
@@ -1138,9 +1138,11 @@ if _OPTIONS["targetos"]=="android" then
"GLESv1_CM",
"GLESv2",
"log",
+ "OpenSLES",
+ "c++_static"
}
linkoptions {
- "-Wl,-soname,libSDL2.so"
+ "-Wl,-soname,libSDL2.so",
}
if _OPTIONS["SEPARATE_BIN"]~="1" then
@@ -1414,8 +1416,10 @@ end
if _OPTIONS["targetos"]=="android" then
files {
- MAME_DIR .. "3rdparty/SDL2/src/audio/android/SDL_androidaudio.c",
+ MAME_DIR .. "3rdparty/SDL2/src/audio/android/opensl_io.h",
+ MAME_DIR .. "3rdparty/SDL2/src/audio/android/opensl_io.c",
MAME_DIR .. "3rdparty/SDL2/src/audio/android/SDL_androidaudio.h",
+ MAME_DIR .. "3rdparty/SDL2/src/audio/android/SDL_androidaudio.c",
MAME_DIR .. "3rdparty/SDL2/src/core/android/SDL_android.c",
MAME_DIR .. "3rdparty/SDL2/src/core/android/SDL_android.h",
MAME_DIR .. "3rdparty/SDL2/src/filesystem/android/SDL_sysfilesystem.c",
diff --git a/scripts/toolchain.lua b/scripts/toolchain.lua
index 46a1b4b22ce..7a8a5c43a19 100644
--- a/scripts/toolchain.lua
+++ b/scripts/toolchain.lua
@@ -836,6 +836,7 @@ function toolchain(_buildDir, _subDir)
includedirs {
MAME_DIR .. "3rdparty/bgfx/3rdparty/khronos",
"$(ANDROID_NDK_ROOT)/sources/cxx-stl/llvm-libc++/libcxx/include",
+ "$(ANDROID_NDK_ROOT)/sources/cxx-stl/llvm-libc++/include",
"$(ANDROID_NDK_ROOT)/sources/android/support/include",
"$(ANDROID_NDK_ROOT)/sources/android/native_app_glue",
}
@@ -852,6 +853,9 @@ function toolchain(_buildDir, _subDir)
"android",
"log",
"c++_static",
+ "c++abi",
+ "android_support",
+ "stdc++",
"gcc",
}
buildoptions {
@@ -879,6 +883,7 @@ function toolchain(_buildDir, _subDir)
configuration { "android-arm" }
libdirs {
"$(ANDROID_NDK_ROOT)/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a",
+ "$(ANDROID_NDK_ARM)/lib/gcc/arm-linux-androideabi/4.9.x/armv7-a",
"$(ANDROID_NDK_ROOT)/platforms/" .. androidPlatform .. "/arch-arm/usr/lib",
}
includedirs {
@@ -892,6 +897,9 @@ function toolchain(_buildDir, _subDir)
"-mfpu=vfpv3-d16",
"-mthumb",
}
+ links {
+ "unwind",
+ }
linkoptions {
"-gcc-toolchain $(ANDROID_NDK_ARM)",
"--sysroot=$(ANDROID_NDK_ROOT)/platforms/" .. androidPlatform .. "/arch-arm",
diff --git a/src/osd/sdl/sdlmain.cpp b/src/osd/sdl/sdlmain.cpp
index 10e57ec5766..3f4b3815359 100644
--- a/src/osd/sdl/sdlmain.cpp
+++ b/src/osd/sdl/sdlmain.cpp
@@ -163,6 +163,9 @@ const options_entry sdl_options::s_option_entries[] =
sdl_options::sdl_options()
: osd_options()
{
+#if defined (SDLMAME_ANDROID)
+ chdir (SDL_AndroidGetExternalStoragePath());
+#endif
std::string ini_path(INI_PATH);
add_entries(sdl_options::s_option_entries);
strreplace(ini_path,"APP_NAME", emulator_info::get_appname_lower());