WIP audio recording, it crashes yet.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user