Added audio support in SDL. Warning: NOT TESTED, even on emulator!

This commit is contained in:
pelya
2009-12-08 15:15:38 +02:00
parent 24e5bd20b1
commit ec3ac50bdb
10 changed files with 420 additions and 41 deletions

View File

@@ -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 \

View File

@@ -31,7 +31,7 @@
#include <stdint.h>
#define SDL_AUDIO_DRIVER_DUMMY 1
#define SDL_AUDIO_DRIVER_ANDROID 1
#define SDL_CDROM_DISABLED 1

View File

@@ -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
};

View File

@@ -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;

View File

@@ -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 <jni.h>
#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;
};

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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