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
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
echo
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 AppUsesGyroscope=$AppUsesGyroscope >> AndroidAppSettings.cfg
echo AppUsesMultitouch=$AppUsesMultitouch >> AndroidAppSettings.cfg
echo AppRecordsAudio=$AppRecordsAudio >> AndroidAppSettings.cfg
echo NonBlockingSwapBuffers=$NonBlockingSwapBuffers >> AndroidAppSettings.cfg
echo RedefinedKeys=\"$RedefinedKeys\" >> AndroidAppSettings.cfg
echo AppTouchscreenKeyboardKeysAmount=$AppTouchscreenKeyboardKeysAmount >> AndroidAppSettings.cfg
@@ -1004,6 +1017,10 @@ else
cat $F | sed "s/package .*;/package $AppFullName;/" >> project/src/Advertisement.java
fi
if [ "$AppRecordsAudio" = "n" -o -z "$AppRecordsAudio" ] ; then
sed -i "/==RECORD_AUDIO==/ d" project/AndroidManifest.xml
fi
case "$MinimumScreenSize" in
n|m)
sed -i "/==SCREEN-SIZE-SMALL==/ d" project/AndroidManifest.xml

View File

@@ -29,9 +29,11 @@
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15"/>
<uses-permission android:name="android.permission.INTERNET"></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>
<!-- <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-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" />

View File

@@ -32,6 +32,8 @@ import android.view.WindowManager;
import android.media.AudioTrack;
import android.media.AudioManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import java.io.*;
import android.util.Log;
import java.lang.Thread;
@@ -132,9 +134,12 @@ class AudioThread
if( mAudio != null )
{
mAudio.pause();
return 1;
}
return 0;
if( mRecorder != null )
{
mRecorder.stop();
}
return 1;
}
public int resumeAudioPlayback()
@@ -142,11 +147,118 @@ class AudioThread
if( mAudio != null )
{
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
AppUsesGyroscope=y
AppUsesMultitouch=y
AppRecordsAudio=y
NonBlockingSwapBuffers=n
RedefinedKeys="SPACE SPACE NO_REMAP NO_REMAP RETURN ESCAPE LCTRL"
AppTouchscreenKeyboardKeysAmount=5
@@ -42,8 +43,8 @@ HiddenMenuOptions='OptionalDownloadConfig DisplaySizeConfig'
FirstStartMenuOptions=''
MultiABI=y
AppMinimumRAM=300
AppVersionCode=08820
AppVersionName="0.8.8.20"
AppVersionCode=08821
AppVersionName="0.8.8.21"
ResetSdlConfigForThisVersion=n
DeleteFilesOnUpgrade="libsdl-DownloadFinished-10.flag"
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 \
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 && \
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

View File

@@ -25,6 +25,7 @@
#include "SDL_video.h"
#include "SDL_screenkeyboard.h"
#include "SDL_audio.h"
#include <jni.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 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
}
#endif

View File

@@ -146,6 +146,12 @@ static jmethodID JavaDeinitAudio = NULL;
static jmethodID JavaPauseAudioPlayback = NULL;
static jmethodID JavaResumeAudioPlayback = NULL;
// Audio recording
static SDL_AudioSpec recording;
static jmethodID JavaStartRecording = NULL;
static jmethodID JavaStopRecording = NULL;
static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS)
{
@@ -199,7 +205,7 @@ static int ANDROIDAUD_OpenAudio (_THIS, SDL_AudioSpec *spec)
if( !jniEnv )
{
__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
@@ -312,6 +318,10 @@ static void ANDROIDAUD_ThreadInit(_THIS)
//__android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_ThreadInit()");
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)
@@ -322,19 +332,19 @@ static void ANDROIDAUD_ThreadDeinit(_THIS)
static void ANDROIDAUD_SendAudioToJava(void)
{
//__android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_PlayAudio()");
jboolean isCopy = JNI_TRUE;
//jboolean isCopy = JNI_TRUE;
(*jniEnvPlaying)->ReleaseByteArrayElements(jniEnvPlaying, audioBufferJNI, (jbyte *)audioBuffer, 0);
audioBuffer = NULL;
(*jniEnvPlaying)->CallIntMethod( jniEnvPlaying, JavaAudioThread, JavaFillBuffer );
audioBuffer = (unsigned char *) (*jniEnvPlaying)->GetByteArrayElements(jniEnvPlaying, audioBufferJNI, &isCopy);
audioBuffer = (unsigned char *) (*jniEnvPlaying)->GetByteArrayElements(jniEnvPlaying, audioBufferJNI, NULL);
if( !audioBuffer )
__android_log_print(ANDROID_LOG_ERROR, "libSDL", "ANDROIDAUD_PlayAudio() JNI::GetByteArrayElements() failed! we will crash now");
if( isCopy == JNI_TRUE )
__android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_PlayAudio() JNI returns a copy of byte array - that's slow");
//if( isCopy == JNI_TRUE )
// __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
@@ -403,3 +413,51 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved)
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 );
}