WIP audio recording, it crashes yet.

This commit is contained in:
pelya
2013-03-10 23:31:41 +02:00
parent afce1cbde2
commit 6852234e92
7 changed files with 219 additions and 16 deletions

View File

@@ -359,6 +359,18 @@ if [ -n "$var" ] ; then
fi fi
fi fi
if [ -z "$AppRecordsAudio" -o -z "$AUTO" ]; then
echo
echo "Application records audio (it will use any available source, such a s microphone)"
echo "API is defined in file SDL_android.h: int SDL_ANDROID_OpenAudioRecording(SDL_AudioSpec *spec); void SDL_ANDROID_CloseAudioRecording(void);"
echo -n "This option will add additional permission to Android manifest ($AppRecordsAudio): "
read var
if [ -n "$var" ] ; then
AppRecordsAudio="$var"
CHANGED=1
fi
fi
if [ -z "$NonBlockingSwapBuffers" -o -z "$AUTO" ]; then if [ -z "$NonBlockingSwapBuffers" -o -z "$AUTO" ]; then
echo echo
echo "Application implements Android-specific routines to put to background, and will not draw anything to screen" echo "Application implements Android-specific routines to put to background, and will not draw anything to screen"
@@ -723,6 +735,7 @@ echo AppUsesJoystick=$AppUsesJoystick >> AndroidAppSettings.cfg
echo AppUsesAccelerometer=$AppUsesAccelerometer >> AndroidAppSettings.cfg echo AppUsesAccelerometer=$AppUsesAccelerometer >> AndroidAppSettings.cfg
echo AppUsesGyroscope=$AppUsesGyroscope >> AndroidAppSettings.cfg echo AppUsesGyroscope=$AppUsesGyroscope >> AndroidAppSettings.cfg
echo AppUsesMultitouch=$AppUsesMultitouch >> AndroidAppSettings.cfg echo AppUsesMultitouch=$AppUsesMultitouch >> AndroidAppSettings.cfg
echo AppRecordsAudio=$AppRecordsAudio >> AndroidAppSettings.cfg
echo NonBlockingSwapBuffers=$NonBlockingSwapBuffers >> AndroidAppSettings.cfg echo NonBlockingSwapBuffers=$NonBlockingSwapBuffers >> AndroidAppSettings.cfg
echo RedefinedKeys=\"$RedefinedKeys\" >> AndroidAppSettings.cfg echo RedefinedKeys=\"$RedefinedKeys\" >> AndroidAppSettings.cfg
echo AppTouchscreenKeyboardKeysAmount=$AppTouchscreenKeyboardKeysAmount >> AndroidAppSettings.cfg echo AppTouchscreenKeyboardKeysAmount=$AppTouchscreenKeyboardKeysAmount >> AndroidAppSettings.cfg
@@ -1004,6 +1017,10 @@ else
cat $F | sed "s/package .*;/package $AppFullName;/" >> project/src/Advertisement.java cat $F | sed "s/package .*;/package $AppFullName;/" >> project/src/Advertisement.java
fi fi
if [ "$AppRecordsAudio" = "n" -o -z "$AppRecordsAudio" ] ; then
sed -i "/==RECORD_AUDIO==/ d" project/AndroidManifest.xml
fi
case "$MinimumScreenSize" in case "$MinimumScreenSize" in
n|m) n|m)
sed -i "/==SCREEN-SIZE-SMALL==/ d" project/AndroidManifest.xml sed -i "/==SCREEN-SIZE-SMALL==/ d" project/AndroidManifest.xml

View File

@@ -29,9 +29,11 @@
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15"/> <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15"/>
<uses-permission android:name="android.permission.INTERNET"></uses-permission> <uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!-- ==RECORD_AUDIO== --> <uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>
<!-- ==ADMOB== --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> <!-- ==ADMOB== --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<!-- <uses-permission android:name="android.permission.VIBRATE"></uses-permission> --> <!-- Vibrator not supported yet by SDL --> <!-- <uses-permission android:name="android.permission.VIBRATE"></uses-permission> --> <!-- Vibrator not supported yet by SDL -->
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" /> <!-- Allow TV boxes -->
<!-- ==SCREEN-SIZE-SMALL== --> <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" /> <!-- ==SCREEN-SIZE-SMALL== --> <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" />
<!-- ==SCREEN-SIZE-NORMAL== --> <supports-screens android:smallScreens="false" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" /> <!-- ==SCREEN-SIZE-NORMAL== --> <supports-screens android:smallScreens="false" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" />
<!-- ==SCREEN-SIZE-LARGE== --> <supports-screens android:smallScreens="false" android:normalScreens="false" android:largeScreens="true" android:xlargeScreens="true" /> <!-- ==SCREEN-SIZE-LARGE== --> <supports-screens android:smallScreens="false" android:normalScreens="false" android:largeScreens="true" android:xlargeScreens="true" />

View File

@@ -32,6 +32,8 @@ import android.view.WindowManager;
import android.media.AudioTrack; import android.media.AudioTrack;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.AudioFormat; import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import java.io.*; import java.io.*;
import android.util.Log; import android.util.Log;
import java.lang.Thread; import java.lang.Thread;
@@ -132,9 +134,12 @@ class AudioThread
if( mAudio != null ) if( mAudio != null )
{ {
mAudio.pause(); mAudio.pause();
return 1;
} }
return 0; if( mRecorder != null )
{
mRecorder.stop();
}
return 1;
} }
public int resumeAudioPlayback() public int resumeAudioPlayback()
@@ -142,11 +147,118 @@ class AudioThread
if( mAudio != null ) if( mAudio != null )
{ {
mAudio.play(); mAudio.play();
return 1;
} }
return 0; if( mRecorder != null )
{
mRecorder.startRecording();
}
return 1;
} }
private native int nativeAudioInitJavaCallbacks();
}
private native int nativeAudioInitJavaCallbacks();
// ----- Audio recording -----
private AudioRecord mRecorder = null;
private RecordingThread mRecordThread = null;
//private int mRecordSize;
//private int mRecordPos;
private int startRecording(int rate, int channels, int encoding, int bufsize)
{
//mRecordSize = bufsize;
//mRecordPos = 0;
if( mRecordThread != null )
{
System.out.println("SDL: error: application already opened audio recording device");
return 0;
}
channels = ( channels == 1 ) ? AudioFormat.CHANNEL_IN_MONO :
AudioFormat.CHANNEL_IN_STEREO;
encoding = ( encoding == 1 ) ? AudioFormat.ENCODING_PCM_16BIT :
AudioFormat.ENCODING_PCM_8BIT;
int minBufDevice = AudioRecord.getMinBufferSize(rate, channels, encoding);
int minBufferSize = Math.max(bufsize * 8, minBufDevice + (bufsize - (minBufDevice % bufsize)));
System.out.println("SDL: app opened recording device, rate " + rate + " channels " + channels + " sample size " + (encoding+1) + " bufsize " + bufsize + " internal bufsize " + minBufferSize);
try {
mRecorder = new AudioRecord(AudioSource.DEFAULT, rate, channels, encoding, minBufferSize);
} catch (IllegalArgumentException e) {
System.out.println("SDL: error: failed to open recording device!");
return 0;
}
mRecordThread = new RecordingThread(bufsize);
mRecorder.startRecording();
mRecordThread.start();
return minBufferSize;
}
private void stopRecording()
{
if( mRecordThread == null )
{
System.out.println("SDL: error: application already closed audio recording device");
return;
}
mRecordThread.terminate = true;
while( !mRecordThread.stopped )
{
try{
Thread.sleep(100);
} catch (InterruptedException e) {}
}
mRecordThread = null;
mRecorder.stop();
mRecorder.release();
mRecorder = null;
System.out.println("SDL: app closed recording device");
}
private class RecordingThread extends Thread
{
public boolean terminate = false;
public boolean stopped = false;
private byte[] mRecordBuffer;
RecordingThread(int bufsize)
{
super();
mRecordBuffer = new byte[bufsize];
}
public void run()
{
while( !terminate )
{
int got = mRecorder.read(mRecordBuffer, 0, mRecordBuffer.length);
if( got != mRecordBuffer.length )
{
System.out.println("SDL: warning: RecordingThread: mRecorder.read returned short byte count " + got + " for bufsize " + mRecordBuffer.length);
// TODO: record in a loop?
}
System.out.println("SDL: nativeAudioRecordCallback with len " + mRecordBuffer.length);
nativeAudioRecordCallback(mRecordBuffer);
System.out.println("SDL: nativeAudioRecordCallback returned");
}
stopped = true;
}
}
private native void nativeAudioRecordCallback(byte[] buffer);
/*
private int recordRead()
{
mRecordPos += mRecordSize;
if( mRecordPos >= mRecordBuffer.length )
mRecordPos = 0;
mRecorder.read(mRecordBuffer, mRecordPos, mRecordSize);
return mRecordPos;
}
public byte[] getRecordBuffer()
{
return mRecordBuffer;
}
*/
}

View File

@@ -31,6 +31,7 @@ AppUsesJoystick=y
AppUsesAccelerometer=n AppUsesAccelerometer=n
AppUsesGyroscope=y AppUsesGyroscope=y
AppUsesMultitouch=y AppUsesMultitouch=y
AppRecordsAudio=y
NonBlockingSwapBuffers=n NonBlockingSwapBuffers=n
RedefinedKeys="SPACE SPACE NO_REMAP NO_REMAP RETURN ESCAPE LCTRL" RedefinedKeys="SPACE SPACE NO_REMAP NO_REMAP RETURN ESCAPE LCTRL"
AppTouchscreenKeyboardKeysAmount=5 AppTouchscreenKeyboardKeysAmount=5
@@ -42,8 +43,8 @@ HiddenMenuOptions='OptionalDownloadConfig DisplaySizeConfig'
FirstStartMenuOptions='' FirstStartMenuOptions=''
MultiABI=y MultiABI=y
AppMinimumRAM=300 AppMinimumRAM=300
AppVersionCode=08820 AppVersionCode=08821
AppVersionName="0.8.8.20" AppVersionName="0.8.8.21"
ResetSdlConfigForThisVersion=n ResetSdlConfigForThisVersion=n
DeleteFilesOnUpgrade="libsdl-DownloadFinished-10.flag" DeleteFilesOnUpgrade="libsdl-DownloadFinished-10.flag"
CompiledLibraries="sdl_mixer sdl_image freetype curl vorbis ogg" CompiledLibraries="sdl_mixer sdl_image freetype curl vorbis ogg"

View File

@@ -24,7 +24,7 @@ env NO_SHARED_LIBS=1 BUILD_EXECUTABLE=1 V=1 ../setEnvironment-armeabi.sh make -C
../setEnvironment-$1.sh make -j8 -C engine release \ ../setEnvironment-$1.sh make -j8 -C engine release \
PLATFORM=android ARCH=$1 USE_GLES=1 USE_LOCAL_HEADERS=0 \ PLATFORM=android ARCH=$1 USE_GLES=1 USE_LOCAL_HEADERS=0 \
USE_OPENAL=0 USE_CURL=1 USE_CURL_DLOPEN=0 USE_CODEC_VORBIS=1 USE_MUMBLE=0 USE_FREETYPE=1 \ USE_OPENAL=0 USE_VOIP=1 USE_CURL=1 USE_CURL_DLOPEN=0 USE_CODEC_VORBIS=1 USE_MUMBLE=0 USE_FREETYPE=1 \
USE_RENDERER_DLOPEN=0 USE_INTERNAL_ZLIB=0 USE_INTERNAL_JPEG=1 BUILD_RENDERER_REND2=0 && \ USE_RENDERER_DLOPEN=0 USE_INTERNAL_ZLIB=0 USE_INTERNAL_JPEG=1 BUILD_RENDERER_REND2=0 && \
echo "Copying engine/build/release-android-$1/openarena.$1 -> libapplication-$1.so" && \ echo "Copying engine/build/release-android-$1/openarena.$1 -> libapplication-$1.so" && \
cp -f engine/build/release-android-$1/openarena.$1 libapplication-$1.so || exit 1 cp -f engine/build/release-android-$1/openarena.$1 libapplication-$1.so || exit 1

View File

@@ -25,6 +25,7 @@
#include "SDL_video.h" #include "SDL_video.h"
#include "SDL_screenkeyboard.h" #include "SDL_screenkeyboard.h"
#include "SDL_audio.h"
#include <jni.h> #include <jni.h>
#include "begin_code.h" #include "begin_code.h"
@@ -82,6 +83,18 @@ extern DECLSPEC int SDLCALL SDL_ANDROID_RequestNewAdvertisement(void);
extern DECLSPEC JNIEnv* SDL_ANDROID_JniEnv(); extern DECLSPEC JNIEnv* SDL_ANDROID_JniEnv();
extern DECLSPEC jobject SDL_ANDROID_JniVideoObject(); extern DECLSPEC jobject SDL_ANDROID_JniVideoObject();
/*
Open audio recording device, it will use parameters freq, format, channels, size and callback,
and return internal buffer size on success, which you may ignore,
because your callback will always be called with buffer size you specified.
Returns 0 on failure (most probably hardware does not support requested audio rate).
SDL_Init(SDL_INIT_AUDIO) has to be done before calling this function.
*/
extern DECLSPEC int SDLCALL SDL_ANDROID_OpenAudioRecording(SDL_AudioSpec *spec);
/* Close audio recording device, SDL_Init(SDL_INIT_AUDIO) has to be done before calling this function. */
extern DECLSPEC void SDLCALL SDL_ANDROID_CloseAudioRecording(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -146,6 +146,12 @@ static jmethodID JavaDeinitAudio = NULL;
static jmethodID JavaPauseAudioPlayback = NULL; static jmethodID JavaPauseAudioPlayback = NULL;
static jmethodID JavaResumeAudioPlayback = NULL; static jmethodID JavaResumeAudioPlayback = NULL;
// Audio recording
static SDL_AudioSpec recording;
static jmethodID JavaStartRecording = NULL;
static jmethodID JavaStopRecording = NULL;
static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS) static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS)
{ {
@@ -199,7 +205,7 @@ static int ANDROIDAUD_OpenAudio (_THIS, SDL_AudioSpec *spec)
if( !jniEnv ) if( !jniEnv )
{ {
__android_log_print(ANDROID_LOG_ERROR, "libSDL", "ANDROIDAUD_OpenAudio: Java VM AttachCurrentThread() failed"); __android_log_print(ANDROID_LOG_ERROR, "libSDL", "ANDROIDAUD_OpenAudio: Java VM AttachCurrentThread() failed");
return (-1); // TODO: enable format conversion? Don't know how to do that in SDL return (-1);
} }
// The returned audioBufferSize may be huge, up to 100 Kb for 44100 because user may have selected large audio buffer to get rid of choppy sound // The returned audioBufferSize may be huge, up to 100 Kb for 44100 because user may have selected large audio buffer to get rid of choppy sound
@@ -312,6 +318,10 @@ static void ANDROIDAUD_ThreadInit(_THIS)
//__android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_ThreadInit()"); //__android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_ThreadInit()");
SDL_memset(audioBuffer, this->spec.silence, this->spec.size); SDL_memset(audioBuffer, this->spec.silence, this->spec.size);
// Audio recording
JavaStartRecording = (*jniEnvPlaying)->GetMethodID(jniEnvPlaying, JavaAudioThreadClass, "startRecording", "(IIII)I");
JavaStopRecording = (*jniEnvPlaying)->GetMethodID(jniEnvPlaying, JavaAudioThreadClass, "stopRecording", "()V");
}; };
static void ANDROIDAUD_ThreadDeinit(_THIS) static void ANDROIDAUD_ThreadDeinit(_THIS)
@@ -322,19 +332,19 @@ static void ANDROIDAUD_ThreadDeinit(_THIS)
static void ANDROIDAUD_SendAudioToJava(void) static void ANDROIDAUD_SendAudioToJava(void)
{ {
//__android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_PlayAudio()"); //__android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_PlayAudio()");
jboolean isCopy = JNI_TRUE; //jboolean isCopy = JNI_TRUE;
(*jniEnvPlaying)->ReleaseByteArrayElements(jniEnvPlaying, audioBufferJNI, (jbyte *)audioBuffer, 0); (*jniEnvPlaying)->ReleaseByteArrayElements(jniEnvPlaying, audioBufferJNI, (jbyte *)audioBuffer, 0);
audioBuffer = NULL; audioBuffer = NULL;
(*jniEnvPlaying)->CallIntMethod( jniEnvPlaying, JavaAudioThread, JavaFillBuffer ); (*jniEnvPlaying)->CallIntMethod( jniEnvPlaying, JavaAudioThread, JavaFillBuffer );
audioBuffer = (unsigned char *) (*jniEnvPlaying)->GetByteArrayElements(jniEnvPlaying, audioBufferJNI, &isCopy); audioBuffer = (unsigned char *) (*jniEnvPlaying)->GetByteArrayElements(jniEnvPlaying, audioBufferJNI, NULL);
if( !audioBuffer ) if( !audioBuffer )
__android_log_print(ANDROID_LOG_ERROR, "libSDL", "ANDROIDAUD_PlayAudio() JNI::GetByteArrayElements() failed! we will crash now"); __android_log_print(ANDROID_LOG_ERROR, "libSDL", "ANDROIDAUD_PlayAudio() JNI::GetByteArrayElements() failed! we will crash now");
if( isCopy == JNI_TRUE ) //if( isCopy == JNI_TRUE )
__android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_PlayAudio() JNI returns a copy of byte array - that's slow"); // __android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_PlayAudio() JNI returns a copy of byte array - that's slow");
} }
#ifdef SDL_AUDIO_APP_IGNORES_RETURNED_BUFFER_SIZE #ifdef SDL_AUDIO_APP_IGNORES_RETURNED_BUFFER_SIZE
@@ -403,3 +413,51 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved)
jniVM = vm; jniVM = vm;
}; };
// ----- Audio recording -----
JNIEXPORT void JNICALL JAVA_EXPORT_NAME(AudioThread_nativeAudioRecordCallback) (JNIEnv * jniEnv, jobject thiz, jbyteArray jbuffer)
{
int len = (*jniEnv)->GetArrayLength(jniEnv, jbuffer);
Uint8 *buffer = (Uint8 *) (*jniEnv)->GetByteArrayElements(jniEnv, jbuffer, NULL);
if( !buffer )
__android_log_print(ANDROID_LOG_ERROR, "libSDL", "AudioThread_nativeAudioRecordCallbacks() JNI::GetByteArrayElements() failed! we will crash now");
__android_log_print(ANDROID_LOG_INFO, "libSDL", "AudioThread_nativeAudioRecordCallbacks(): got buffer %p len %d", buffer, len);
recording.callback(recording.userdata, buffer, len);
(*jniEnv)->ReleaseByteArrayElements(jniEnv, jbuffer, (jbyte *)buffer, 0);
}
extern DECLSPEC int SDLCALL SDL_ANDROID_OpenAudioRecording(SDL_AudioSpec *spec)
{
JNIEnv * jniEnv = NULL;
recording = *spec;
if( ! (recording.format == AUDIO_S8 || recording.format == AUDIO_S16) )
{
__android_log_print(ANDROID_LOG_ERROR, "libSDL", "Application requested unsupported audio format - only S8 and S16 are supported");
return 0;
}
if( ! recording.callback )
{
__android_log_print(ANDROID_LOG_ERROR, "libSDL", "Application did not provide callback");
return 0;
}
(*jniVM)->AttachCurrentThread(jniVM, &jniEnv, NULL);
return (*jniEnv)->CallIntMethod( jniEnv, JavaAudioThread, JavaStartRecording, (jint)recording.freq, (jint)recording.channels,
(jint)(recording.format == AUDIO_S16 ? 1 : 0), (jint)recording.size );
}
extern DECLSPEC void SDLCALL SDL_ANDROID_CloseAudioRecording(void)
{
JNIEnv * jniEnv = NULL;
(*jniVM)->AttachCurrentThread(jniVM, &jniEnv, NULL);
(*jniEnv)->CallVoidMethod( jniEnv, JavaAudioThread, JavaStopRecording );
}