diff --git a/alienblaster/project/jni/sdl/Android.mk b/alienblaster/project/jni/sdl/Android.mk index 195cec005..1373a3024 100644 --- a/alienblaster/project/jni/sdl/Android.mk +++ b/alienblaster/project/jni/sdl/Android.mk @@ -26,7 +26,7 @@ SDL_SRCS := \ src/thread/*.c \ src/timer/*.c \ src/video/*.c \ - src/audio/dummy/*.c \ + src/audio/android/*.c \ src/video/android/*.c \ src/joystick/dummy/*.c \ src/cdrom/dummy/*.c \ diff --git a/alienblaster/project/jni/sdl/include/SDL_config_android.h b/alienblaster/project/jni/sdl/include/SDL_config_android.h index 82c4652ef..0b8b08bbe 100644 --- a/alienblaster/project/jni/sdl/include/SDL_config_android.h +++ b/alienblaster/project/jni/sdl/include/SDL_config_android.h @@ -31,7 +31,7 @@ #include -#define SDL_AUDIO_DRIVER_DUMMY 1 +#define SDL_AUDIO_DRIVER_ANDROID 1 #define SDL_CDROM_DISABLED 1 diff --git a/alienblaster/project/jni/sdl/src/audio/SDL_audio.c b/alienblaster/project/jni/sdl/src/audio/SDL_audio.c index bdeacdc4e..9e0a1aae4 100644 --- a/alienblaster/project/jni/sdl/src/audio/SDL_audio.c +++ b/alienblaster/project/jni/sdl/src/audio/SDL_audio.c @@ -112,6 +112,9 @@ static AudioBootStrap *bootstrap[] = { #endif #if SDL_AUDIO_DRIVER_EPOCAUDIO &EPOCAudio_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_ANDROID + &ANDROIDAUD_bootstrap, #endif NULL }; diff --git a/alienblaster/project/jni/sdl/src/audio/SDL_sysaudio.h b/alienblaster/project/jni/sdl/src/audio/SDL_sysaudio.h index 50cf17985..189cb321f 100644 --- a/alienblaster/project/jni/sdl/src/audio/SDL_sysaudio.h +++ b/alienblaster/project/jni/sdl/src/audio/SDL_sysaudio.h @@ -177,6 +177,9 @@ extern AudioBootStrap DART_bootstrap; #if SDL_AUDIO_DRIVER_EPOCAUDIO extern AudioBootStrap EPOCAudio_bootstrap; #endif +#if SDL_AUDIO_DRIVER_ANDROID +extern AudioBootStrap ANDROIDAUD_bootstrap; +#endif /* This is the current audio device */ extern SDL_AudioDevice *current_audio; diff --git a/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c b/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c new file mode 100644 index 000000000..1644d1ff2 --- /dev/null +++ b/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c @@ -0,0 +1,252 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org + + This file written by Ryan C. Gordon (icculus@icculus.org) +*/ +#include "SDL_config.h" + +/* Output audio to nowhere... */ + +#include "SDL_rwops.h" +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_androidaudio.h" +#include "SDL_mutex.h" +#include "SDL_thread.h" +#include + +#define ANDROIDAUD_DRIVER_NAME "android" + +/* Audio driver functions */ +static int ANDROIDAUD_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void ANDROIDAUD_WaitAudio(_THIS); +static void ANDROIDAUD_PlayAudio(_THIS); +static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS); +static void ANDROIDAUD_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ +static int ANDROIDAUD_Available(void) +{ + return(1); +} + +static void ANDROIDAUD_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *ANDROIDAUD_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = ANDROIDAUD_OpenAudio; + this->WaitAudio = ANDROIDAUD_WaitAudio; + this->PlayAudio = ANDROIDAUD_PlayAudio; + this->GetAudioBuf = ANDROIDAUD_GetAudioBuf; + this->CloseAudio = ANDROIDAUD_CloseAudio; + + this->free = ANDROIDAUD_DeleteDevice; + + return this; +} + +AudioBootStrap ANDROIDAUD_bootstrap = { + ANDROIDAUD_DRIVER_NAME, "SDL Android audio driver", + ANDROIDAUD_Available, ANDROIDAUD_CreateDevice +}; + + +static SDL_mutex * audioMutex = NULL; +static SDL_cond * audioCond = NULL; +static unsigned char * audioBuffer = NULL; +static size_t audioBufferSize = 0; +static SDL_AudioSpec *audioFormat = NULL; +static int audioInitialized = 0; + +/* This function waits until it is possible to write a full sound buffer */ +static void ANDROIDAUD_WaitAudio(_THIS) +{ + /* We will block in PlayAudio(), do nothing here */ +} + +static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS) +{ + return(this->hidden->mixbuf); +} + +static void ANDROIDAUD_CloseAudio(_THIS) +{ + SDL_mutex * audioMutex1; + + if ( this->hidden->mixbuf != NULL ) { + SDL_FreeAudioMem(this->hidden->mixbuf); + this->hidden->mixbuf = NULL; + } + if( audioMutex != NULL ) + { + audioMutex1 = audioMutex; + SDL_mutexP(audioMutex1); + audioInitialized = 0; + SDL_CondSignal(audioCond); + audioMutex = NULL; + SDL_DestroyCond(audioCond); + audioCond = NULL; + audioFormat = NULL; + audioBuffer = NULL; + audioBufferSize = 0; + SDL_mutexV(audioMutex1); + SDL_DestroyMutex(audioMutex1); + } +} + +static int ANDROIDAUD_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + if( ! (spec->format == AUDIO_S8 || spec->format == AUDIO_S16) ) + return (-1); // TODO: enable format conversion? Don't know how to do that in SDL + + /* Allocate mixing buffer */ + this->hidden->mixlen = spec->size; + this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); + if ( this->hidden->mixbuf == NULL ) { + return(-1); + } + SDL_memset(this->hidden->mixbuf, spec->silence, spec->size); + + if( audioMutex == NULL ) + { + audioInitialized = 0; + audioFormat = spec; + audioBuffer = this->hidden->mixbuf; + audioBufferSize = this->hidden->mixlen; + audioMutex = SDL_CreateMutex(); + audioCond = SDL_CreateCond(); + } + + SDL_mutexP(audioMutex); + + while( !audioInitialized ) + SDL_CondWait( audioCond, audioMutex ); + + audioFormat = NULL; + + SDL_mutexV(audioMutex); + + return(0); +} + +static void ANDROIDAUD_PlayAudio(_THIS) +{ + SDL_mutexP(audioMutex); + + audioBuffer = this->hidden->mixbuf; + audioBufferSize = this->hidden->mixlen; + + while( audioBuffer != NULL ) + SDL_CondWait( audioCond, audioMutex ); + + SDL_mutexV(audioMutex); +} + +#ifndef SDL_JAVA_PACKAGE_PATH +#error You have to define SDL_JAVA_PACKAGE_PATH to your package path with dots replaced with underscores, for example "com_example_SanAngeles" +#endif +#define JAVA_EXPORT_NAME2(name,package) Java_##package##_##name +#define JAVA_EXPORT_NAME1(name,package) JAVA_EXPORT_NAME2(name,package) +#define JAVA_EXPORT_NAME(name) JAVA_EXPORT_NAME1(name,SDL_JAVA_PACKAGE_PATH) + +extern jintArray JAVA_EXPORT_NAME(nativeAudioInit) (JNIEnv * env, jobject jobj) +{ + jintArray ret = NULL; + int initData[4] = { 0, 0, 0, 0 }; // { rate, channels, encoding, bufsize }; + + if( audioMutex == NULL ) + return; + + SDL_mutexP(audioMutex); + + if( audioInitialized == 0 ) + { + initData[0] = audioFormat->freq; + initData[1] = audioFormat->channels; + initData[2] = ( audioFormat->format == AUDIO_S16 ) ? 1 : 0; + initData[3] = audioFormat->size; + ret=(*env)->NewIntArray(env, 4); + (*env)->SetIntArrayRegion(env, ret, 0, 4, (jint *)initData); + audioInitialized = 1; + SDL_CondSignal(audioCond); + } + + SDL_mutexV(audioMutex); + + return (ret); +}; + +extern jint JAVA_EXPORT_NAME(nativeAudioBuffer) ( JNIEnv * env, jobject jobj, jbyteArray data ) +{ + int ret = 0; + + if( audioMutex == NULL ) + return; + + SDL_mutexP(audioMutex); + + if( !audioInitialized ) + ret = -1; + + if( audioBuffer == NULL ) + { + ret = 0; + } + else + { + (*env)->SetByteArrayRegion(env, data, 0, audioBufferSize, (jbyte *)audioBuffer); + ret = audioBufferSize; + audioBuffer = NULL; + audioBufferSize = 0; + SDL_CondSignal(audioCond); + } + + SDL_mutexV(audioMutex); + + return ret; +}; + diff --git a/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.h b/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.h new file mode 100644 index 000000000..80a6129c1 --- /dev/null +++ b/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.h @@ -0,0 +1,38 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_androidaudio_h +#define _SDL_androidaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + Uint8 *mixbuf; + Uint32 mixlen; +}; + +#endif /* _SDL_androidaudio_h */ diff --git a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c index 1c7745556..4357545c2 100644 --- a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c +++ b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c @@ -77,6 +77,17 @@ static int ANDROID_FlipHWSurface(_THIS, SDL_Surface *surface); static void ANDROID_UpdateRects(_THIS, int numrects, SDL_Rect *rects); +#define SDL_NUMMODES 3 + +/* Private display data */ + +struct SDL_PrivateVideoData { + SDL_Rect *SDL_modelist[SDL_NUMMODES+1]; +}; + +#define SDL_modelist (this->hidden->SDL_modelist) + + static int sWindowWidth = 320; static int sWindowHeight = 480; static SDL_mutex * WaitForNativeRender = NULL; @@ -734,12 +745,13 @@ void ANDROID_InitOSKeymap(_THIS) keymap[KEYCODE_UNKNOWN] = SDLK_UNKNOWN; - keymap[KEYCODE_MENU] = SDLK_ESCAPE; + keymap[KEYCODE_BACK] = SDLK_ESCAPE; + keymap[KEYCODE_MENU] = SDLK_LALT; keymap[KEYCODE_CALL] = SDLK_LCTRL; keymap[KEYCODE_ENDCALL] = SDLK_LSHIFT; keymap[KEYCODE_CAMERA] = SDLK_RSHIFT; - keymap[KEYCODE_POWER] = SDLK_LALT; + keymap[KEYCODE_POWER] = SDLK_RALT; keymap[KEYCODE_BACK] = SDLK_ESCAPE; // Note: generates SDL_QUIT keymap[KEYCODE_0] = SDLK_0; diff --git a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.h b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.h index 1f6f08109..d4f8482ff 100644 --- a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.h +++ b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.h @@ -29,16 +29,6 @@ /* Hidden "this" pointer for the video functions */ #define _THIS SDL_VideoDevice *this -#define SDL_NUMMODES 3 - -/* Private display data */ - -struct SDL_PrivateVideoData { - SDL_Rect *SDL_modelist[SDL_NUMMODES+1]; -}; - -#define SDL_modelist (this->hidden->SDL_modelist) - extern void ANDROID_InitOSKeymap(_THIS); extern void ANDROID_PumpEvents(_THIS); diff --git a/alienblaster/project/src/DemoActivity.java b/alienblaster/project/src/DemoActivity.java index 72f50afb8..144b570f8 100644 --- a/alienblaster/project/src/DemoActivity.java +++ b/alienblaster/project/src/DemoActivity.java @@ -50,6 +50,9 @@ import android.opengl.GLSurfaceView; import android.os.Bundle; import android.view.MotionEvent; import android.view.KeyEvent; +import android.media.AudioTrack; +import android.media.AudioManager; +import android.media.AudioFormat; class LoadLibrary { public LoadLibrary() {} @@ -81,6 +84,7 @@ class DemoRenderer implements GLSurfaceView.Renderer { private static native void nativeResize(int w, int h); private static native void nativeRender(); private static native void nativeDone(); + } class DemoGLSurfaceView extends GLSurfaceView { @@ -105,23 +109,7 @@ class DemoGLSurfaceView extends GLSurfaceView { } return true; } - - @Override - public boolean onKeyDown(int keyCode, final KeyEvent event) { - if(keyCode == KeyEvent.KEYCODE_BACK) { - mParent.finish(); - } else { - nativeKey( keyCode, 1 ); - } - return true; - } - - @Override - public boolean onKeyUp(int keyCode, final KeyEvent event) { - nativeKey( keyCode, 0 ); - return true; - } - + public void exitApp() { mRenderer.exitApp(); }; @@ -129,8 +117,74 @@ class DemoGLSurfaceView extends GLSurfaceView { DemoRenderer mRenderer; Activity mParent; - private static native void nativeMouse( int x, int y, int action ); - private static native void nativeKey( int keyCode, int down ); + public static native void nativeMouse( int x, int y, int action ); + public static native void nativeKey( int keyCode, int down ); +} + +class AudioThread extends Thread { + + private Activity mParent; + private AudioTrack mAudio; + private byte[] mAudioBuffer; + + public AudioThread(Activity parent) + { + mParent = parent; + mAudio = null; + mAudioBuffer = null; + } + + @Override + public void run() + { + while( !isInterrupted() ) + { + if( mAudio == null ) + { + int[] initParams = nativeAudioInit(); + if( initParams == null ) + { + try { + sleep(200); + } catch( java.lang.InterruptedException e ) { }; + } + else + { + int rate = initParams[0]; + int channels = initParams[1]; + channels = ( channels == 1 ) ? AudioFormat.CHANNEL_CONFIGURATION_MONO : + AudioFormat.CHANNEL_CONFIGURATION_STEREO; + int encoding = initParams[2]; + encoding = ( encoding == 1 ) ? AudioFormat.ENCODING_PCM_16BIT : + AudioFormat.ENCODING_PCM_8BIT; + int bufSize = AudioTrack.getMinBufferSize( rate, channels, encoding); + if( initParams[3] > bufSize ) + bufSize = initParams[3]; + mAudioBuffer = new byte[bufSize]; + mAudio = new AudioTrack(AudioManager.STREAM_MUSIC, + rate, + channels, + encoding, + bufSize, + AudioTrack.MODE_STREAM ); + mAudio.play(); + } + } + else + { + int len = nativeAudioBuffer( mAudioBuffer ); + if( len > 0 ) + mAudio.write( mAudioBuffer, 0, len ); + if( len < 0 ) + break; + } + } + if( mAudio != null ) + mAudio.stop(); + } + + private static native int[] nativeAudioInit(); + private static native int nativeAudioBuffer( byte[] data ); } public class DemoActivity extends Activity { @@ -138,6 +192,8 @@ public class DemoActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLoadLibraryStub = new LoadLibrary(); + mAudioThread = new AudioThread(this); + mAudioThread.start(); mGLView = new DemoGLSurfaceView(this); setContentView(mGLView); // Receive keyboard events @@ -159,12 +215,31 @@ public class DemoActivity extends Activity { } @Override - protected void onStop() { - super.onStop(); + protected void onStop() + { + mAudioThread.interrupt(); + try { + mAudioThread.join(); + } catch( java.lang.InterruptedException e ) { }; mGLView.exitApp(); + super.onStop(); finish(); } + @Override + public boolean onKeyDown(int keyCode, final KeyEvent event) { + // Overrides Back key to use in our app + mGLView.nativeKey( keyCode, 1 ); + return true; + } + + @Override + public boolean onKeyUp(int keyCode, final KeyEvent event) { + mGLView.nativeKey( keyCode, 0 ); + return true; + } + private DemoGLSurfaceView mGLView; private LoadLibrary mLoadLibraryStub; + private AudioThread mAudioThread; } diff --git a/alienblaster/readme.txt b/alienblaster/readme.txt index 8c66997a6..b6012cf06 100644 --- a/alienblaster/readme.txt +++ b/alienblaster/readme.txt @@ -1,6 +1,6 @@ This is Alien Blaster game ported to Google Android. I did not change anything in Alien Blaster sources, except for SCREEN_WIDTH, -SCREEN_HEIGHT and BIT_DEPTH constants in global.h. +SCREEN_HEIGHT and BIT_DEPTH constants in global.h, to support 320x430x16bpp video mode. This should be compiled with Android 1.6 SDK and NDK - google for them and install them as described in their docs. You'll need to install Eclipse or Ant too @@ -26,17 +26,23 @@ download alienblaster-1.1.0.tgz, unpack it and execute exit adb push alienblaster /data/data/de.schwardtnet.alienblaster/files Then you can test it by launching Alien Blaster icon from Android applications menu. -It's designed for 640x480, but with bit of luck you can play the game a bit. +It's designed for 640x480, and GUI elements are drawn out of place, but you can play the game. Note: You should play it with vertical screen orientation (keyboard is closed) -Fire key is Call key, redefine Choose Weapon to Enter key through (trackball click) +Fire key is Call key ( = left Ctrl for SDL ), Change weapon is Menu key ( = left Alt for SDL ) +Note that you may use Volume up/down and Camera keys as game inputs - +you'll have to redefine them in game keyconfig menu. Other keys like Home, Back and End Call will force application quit, and because the app itself does not handle SDL_QUIT event correctly (asks for confirmation), it will stay in memory until you reboot device. The same will happen if the phone -goes to sleep, so hit keyboard often plz. +goes to sleep, so hit keyboard often please. To exit correctly press Menu key - it's redirected to Escape. -When porting you own app, replace "alienblaster" and "de.schwardtnet.alienblaster" with -the name of your application and your reversed webpage address everywhere: +When porting you own app, first of all ensure that your application supports +one of 320x200, 320x240 or 320x430 display resolutions and 16 bits per pixel +(320x430 is resolution for HTC devices, if other vendors will produce Android phones it may differ). + +Replace all strings "alienblaster" and "de.schwardtnet.alienblaster" with +the name of your application and your reversed webpage address (or any unique string): Application.mk:2 project/AndroidManifest.xml:3 project/src/DemoActivity.java:42 and 57