mirror of
https://github.com/RPCS3/soundtouch.git
synced 2024-11-08 12:02:28 +01:00
Developed more refined Android example application that also works in ARM & X86 platforms.
This commit is contained in:
parent
1040bd1d28
commit
92973bc18e
@ -172,6 +172,7 @@ namespace soundtouch
|
||||
#else
|
||||
// use c++ standard exceptions
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
||||
#endif
|
||||
|
||||
|
@ -8,6 +8,9 @@
|
||||
android:minSdkVersion="11"
|
||||
android:targetSdkVersion="21" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
|
@ -45,49 +45,13 @@
|
||||
open Cygwin/bash shell, go to directory <b>"soundtouch/source/Android-lib/jni"</b> and invoke the NDK
|
||||
compiler with following command:</p>
|
||||
<pre> $NDK/ndk-build</pre>
|
||||
<p>This will build the ARMv5 and ARMv7 versions of SoundTouch library (including
|
||||
also the example JNI
|
||||
interface, see below) into the "libs" subdirectory.</p>
|
||||
<p>This will build binaries for all the supported Android platforms (arm-v5, arm-v7, X86, MIPS etc) of SoundTouch library, plus the JNI wrapper interface as discussed below. The target binaries will be built into the "libs" subdirectory. As long as all these .so binary library versions are included in the APK Application delivery package, the targer Android device can choose the correct library version to use. </p>
|
||||
<p>Notice that to allow Cygwin/bash to locate the NDK compile scripts, you
|
||||
need to define the location of the NDK installation defined in environment
|
||||
variable "NDK". That's easiest done by adding the NDK path definition at end of
|
||||
your <b>~/.bash_profile</b> file, for instance as follows:</p>
|
||||
<pre> NDK=/cygdrive/d/Android/android-ndk-r6</pre>
|
||||
<hr />
|
||||
<h2>
|
||||
Android floating-point performance considerations</h2>
|
||||
<p>
|
||||
Default build target for
|
||||
Android NDK is ARMv5 CPU generation, as that works in
|
||||
all ARM-based Android devices.<p>
|
||||
This has a pitfall though: For ideal sound quality SoundTouch should be compiled
|
||||
to use floating-point algorithms, however, all low-end Android devices do not
|
||||
have floating-point hardware in their CPUs, and hence the default ARMv5 compilation uses software-emulation for floating-point calculations instead of
|
||||
hardware floating-point to allow running the binary executables also in low-end devices.<p>
|
||||
The floating point software-emulation is however several tens of times slower
|
||||
than real hardware-level floating-point calculations, making
|
||||
floating-point-intensive applications such as SoundTouch infeasible with low-end
|
||||
devices.<p>
|
||||
As workaround, the SoundTouch Android compilation builds two separate versions
|
||||
of the library:<ul>
|
||||
<li>ARMv5 version that compiles SoundTouch using integer algorithm version. The integer
|
||||
algorithm version compromises the sound quality but provides good performance also
|
||||
with low-end
|
||||
devices without hardware floating-point support in the CPU level.</li>
|
||||
<li>ARMv7 version that compiles SoundTouch using hardware floating-point algorithms.
|
||||
These algorithms provide ideal sound quality yet do not work in simpler CPU
|
||||
models.</li>
|
||||
</ul>
|
||||
<p>
|
||||
These two library compilations are already defined in file "<b>jni/Application.mk</b>"
|
||||
so that these two separate library targets are automatically built under the "<b>libs</b>"
|
||||
directory. As far as you include both these compiled library versions into your
|
||||
application delivery, the Android devices can automatically select the right
|
||||
library version based on the available device's capabilities.<p>
|
||||
Please yet be aware that depending on capabilities of the Android devices you
|
||||
will need to provide the SoundTouch routines with samples in either integer or
|
||||
floating-point format, so build your interface routines to take this into
|
||||
account.<hr />
|
||||
<h2>
|
||||
Calling SoundTouch native routines from Android application</h2>
|
||||
<p>The NDK tools build the SoundTouch c++ routines into a native binary library, while
|
||||
@ -96,23 +60,42 @@
|
||||
Interface (JNI).</p>
|
||||
<p>
|
||||
The SoundTouch source code package provides source code example how to
|
||||
use JNI to call native c++ routines from a Java class through the following
|
||||
source code file pair:<ul>
|
||||
<li><b>Android-lib/jni/soundtouch-jni.cpp</b>: This file contains c/c++ routine that
|
||||
calls SoundTouch library routine to return the library version string to the main
|
||||
Android application. The NDK compiles this file along with the SoundTouch
|
||||
use JNI to call native c++ routines from a Java class, and provides source codes also for
|
||||
a simple example Android application:<ul>
|
||||
<li><b>ExampleActivity</b>: This is simple Android example application that
|
||||
utilizes SoundTouch native routines for processing WAV audio files. To build the example
|
||||
application, use Eclipse Android SDK environment to import the "ExampleActivity" project in the "Android-lib" folder into the Eclipse workspace.
|
||||
<li><b>Android-lib/jni/soundtouch-jni.cpp</b>: This file contains c/c++ wrapper routines
|
||||
for performing elementary audio file processing with adjusted tempo/pitch/speed parameters
|
||||
from the Android application. The wrapper interface is not complete, but provides example
|
||||
that is easy to extend when necessary. The NDK compiles this file along with the SoundTouch
|
||||
routines into the native binary library.</li>
|
||||
<li><b>Android-lib/src/net/surina/soundtouch/SoundTouch.java</b>: This file provides
|
||||
a Java interface class to load the native library and to invoke the native routine implemented in
|
||||
the file <b>soundtouch-jni.cpp</b></li>
|
||||
<li><b>Android-lib/src/net/surina/soundtouch/SoundTouch.java</b>: This file implements
|
||||
the Java interface class that loasd & accesses the JNI routines in the natively compiled library.
|
||||
The example Android application uses this class as interface for processing audio files
|
||||
with SoundTouch.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Feel free to examine and extend the provided cpp/java source code example file pair to
|
||||
implement and integrate the desired SoundTouch library capabilities into your Android application.</p>
|
||||
implement and integrate the desired SoundTouch library capabilities into your own Android application.</p>
|
||||
<hr />
|
||||
<h2>
|
||||
Android floating-point performance considerations</h2>
|
||||
<p>
|
||||
The make process will build dedicated binaries for each supported Android CPU hardware platform type.
|
||||
</p><p>SoundTouch uses floating-point algorithms for ideal sound quality on all other platform than in the lowest-end ARMv5. That is because lowest-end Android devices are not guaranteed to
|
||||
have floating-point hardware in their CPUs, so that the ARMv5 compilation uses by default software-emulation for floating-point calculations to allow running the binary executables also in low-end devices without floating-point hardware.<p>
|
||||
As floating point software-emulation is however several tens of times slower
|
||||
than real hardware-level floating-point calculations, that would make running
|
||||
floating-point-intensive applications such as SoundTouch infeasible in these low-end
|
||||
devices. As workaround, the SoundTouch Android compilation builds the ARMv5 version using integer algorithm versions. The integer
|
||||
algorithm version compromises the sound quality but provides good performance also
|
||||
with low-end devices without hardware floating-point support in the CPU level.</p>
|
||||
<p>When Android devices with more capable device is used, the device will automatically choose a proper library version for ideal sound quality.</p>
|
||||
<hr />
|
||||
<p style="text-align: center"><i>Copyright © Olli Parviainen</i></p>
|
||||
<!--
|
||||
$Id$
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -23,7 +23,7 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := soundtouch
|
||||
LOCAL_SRC_FILES := soundtouch-jni.cpp ../../SoundTouch/AAFilter.cpp ../../SoundTouch/FIFOSampleBuffer.cpp \
|
||||
../../SoundTouch/FIRFilter.cpp ../../SoundTouch/cpu_detect_x86.cpp \
|
||||
../../SoundTouch/sse_optimized.cpp \
|
||||
../../SoundTouch/sse_optimized.cpp ../../SoundStretch/WavFile.cpp \
|
||||
../../SoundTouch/RateTransposer.cpp ../../SoundTouch/SoundTouch.cpp \
|
||||
../../SoundTouch/InterpolateCubic.cpp ../../SoundTouch/InterpolateLinear.cpp \
|
||||
../../SoundTouch/InterpolateShannon.cpp ../../SoundTouch/TDStretch.cpp \
|
||||
@ -37,7 +37,9 @@ LOCAL_LDLIBS += -llog
|
||||
# for native asset manager
|
||||
#LOCAL_LDLIBS += -landroid
|
||||
# don't export all symbols
|
||||
# added "-marm" switch to use arm instruction set instead of thumb for improved calculation performance.
|
||||
LOCAL_CFLAGS += -fvisibility=hidden -I ../../../include -D ST_NO_EXCEPTION_HANDLING -fdata-sections -ffunction-sections
|
||||
#
|
||||
# in ARM-only environment could add "-marm" switch to force arm instruction set instead
|
||||
# of thumb for improved calculation performance.
|
||||
LOCAL_CFLAGS += -fvisibility=hidden -I ../../../include -fdata-sections -ffunction-sections
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
@ -5,3 +5,5 @@
|
||||
|
||||
APP_ABI := all #armeabi-v7a armeabi
|
||||
APP_OPTIM := release
|
||||
APP_STL := stlport_static
|
||||
APP_CPPFLAGS := -fexceptions
|
@ -14,17 +14,101 @@
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include "../../../include/SoundTouch.h"
|
||||
#include "../source/SoundStretch/WavFile.h"
|
||||
|
||||
#define LOGV(...) __android_log_print((int)ANDROID_LOG_INFO, "SOUNDTOUCH", __VA_ARGS__)
|
||||
//#define LOGV(...)
|
||||
|
||||
|
||||
// String for keeping possible c++ exception error messages. Notice that this isn't
|
||||
// thread-safe but it's expected that exceptions are special situations that won't
|
||||
// occur in several threads in parallel.
|
||||
static string _errMsg = "";
|
||||
|
||||
|
||||
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
|
||||
#define BUFF_SIZE 4096
|
||||
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
|
||||
// Set error message to return
|
||||
static void _setErrmsg(const char *msg)
|
||||
{
|
||||
_errMsg = msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Processes the sound file
|
||||
static void _processFile(SoundTouch *pSoundTouch, const char *inFileName, const char *outFileName)
|
||||
{
|
||||
int nSamples;
|
||||
int nChannels;
|
||||
int buffSizeSamples;
|
||||
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
||||
|
||||
// open input file
|
||||
WavInFile inFile(inFileName);
|
||||
int sampleRate = inFile.getSampleRate();
|
||||
int bits = inFile.getNumBits();
|
||||
nChannels = inFile.getNumChannels();
|
||||
|
||||
// create output file
|
||||
WavOutFile outFile(outFileName, sampleRate, bits, nChannels);
|
||||
|
||||
pSoundTouch->setSampleRate(sampleRate);
|
||||
pSoundTouch->setChannels(nChannels);
|
||||
|
||||
assert(nChannels > 0);
|
||||
buffSizeSamples = BUFF_SIZE / nChannels;
|
||||
|
||||
// Process samples read from the input file
|
||||
while (inFile.eof() == 0)
|
||||
{
|
||||
int num;
|
||||
|
||||
// Read a chunk of samples from the input file
|
||||
num = inFile.read(sampleBuffer, BUFF_SIZE);
|
||||
nSamples = num / nChannels;
|
||||
|
||||
// Feed the samples into SoundTouch processor
|
||||
pSoundTouch->putSamples(sampleBuffer, nSamples);
|
||||
|
||||
// Read ready samples from SoundTouch processor & write them output file.
|
||||
// NOTES:
|
||||
// - 'receiveSamples' doesn't necessarily return any samples at all
|
||||
// during some rounds!
|
||||
// - On the other hand, during some round 'receiveSamples' may have more
|
||||
// ready samples than would fit into 'sampleBuffer', and for this reason
|
||||
// the 'receiveSamples' call is iterated for as many times as it
|
||||
// outputs samples.
|
||||
do
|
||||
{
|
||||
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
||||
outFile.write(sampleBuffer, nSamples * nChannels);
|
||||
} while (nSamples != 0);
|
||||
}
|
||||
|
||||
// Now the input file is processed, yet 'flush' few last samples that are
|
||||
// hiding in the SoundTouch's internal processing pipeline.
|
||||
pSoundTouch->flush();
|
||||
do
|
||||
{
|
||||
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
||||
outFile.write(sampleBuffer, nSamples * nChannels);
|
||||
} while (nSamples != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" DLL_PUBLIC jstring Java_net_surina_soundtouch_SoundTouch_getVersionString(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
const char *verStr;
|
||||
@ -37,3 +121,77 @@ extern "C" DLL_PUBLIC jstring Java_net_surina_soundtouch_SoundTouch_getVersionSt
|
||||
// return version as string
|
||||
return env->NewStringUTF(verStr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" DLL_PUBLIC jlong Java_net_surina_soundtouch_SoundTouch_newInstance(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
return (jlong)(new SoundTouch());
|
||||
}
|
||||
|
||||
|
||||
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_deleteInstance(JNIEnv *env, jobject thiz, jlong handle)
|
||||
{
|
||||
SoundTouch *ptr = (SoundTouch*)handle;
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
|
||||
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setTempo(JNIEnv *env, jobject thiz, jlong handle, jfloat tempo)
|
||||
{
|
||||
SoundTouch *ptr = (SoundTouch*)handle;
|
||||
ptr->setTempo(tempo);
|
||||
}
|
||||
|
||||
|
||||
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setPitchSemiTones(JNIEnv *env, jobject thiz, jlong handle, jfloat pitch)
|
||||
{
|
||||
SoundTouch *ptr = (SoundTouch*)handle;
|
||||
ptr->setPitchSemiTones(pitch);
|
||||
}
|
||||
|
||||
|
||||
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setSpeed(JNIEnv *env, jobject thiz, jlong handle, jfloat speed)
|
||||
{
|
||||
SoundTouch *ptr = (SoundTouch*)handle;
|
||||
ptr->setRate(speed);
|
||||
}
|
||||
|
||||
|
||||
extern "C" DLL_PUBLIC jstring Java_net_surina_soundtouch_SoundTouch_getErrorString(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
jstring result = env->NewStringUTF(_errMsg.c_str());
|
||||
_errMsg.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
extern "C" DLL_PUBLIC int Java_net_surina_soundtouch_SoundTouch_processFile(JNIEnv *env, jobject thiz, jlong handle, jstring jinputFile, jstring joutputFile)
|
||||
{
|
||||
SoundTouch *ptr = (SoundTouch*)handle;
|
||||
|
||||
const char *inputFile = env->GetStringUTFChars(jinputFile, 0);
|
||||
const char *outputFile = env->GetStringUTFChars(joutputFile, 0);
|
||||
|
||||
LOGV("JNI process file %s", inputFile);
|
||||
|
||||
try
|
||||
{
|
||||
_processFile(ptr, inputFile, outputFile);
|
||||
}
|
||||
catch (const runtime_error &e)
|
||||
{
|
||||
const char *err = e.what();
|
||||
// An exception occurred during processing, return the error message
|
||||
LOGV("JNI exception in SoundTouch::processFile: %s", err);
|
||||
_setErrmsg(err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
env->ReleaseStringUTFChars(jinputFile, inputFile);
|
||||
env->ReleaseStringUTFChars(joutputFile, outputFile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
android:id="@+id/editTextTempo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:ems="5"
|
||||
android:inputType="numberDecimal"
|
||||
android:text="100" >
|
||||
</EditText>
|
||||
@ -38,45 +38,24 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pitch steps:"
|
||||
android:text="Pitch half-steps:"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextPitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="numberDecimal"
|
||||
android:ems="5"
|
||||
android:inputType="numberSigned"
|
||||
android:text="0" >
|
||||
</EditText>
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/tableRow3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Speed % :"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextSpeed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="numberDecimal"
|
||||
android:text="100" >
|
||||
</EditText>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="20dp" >
|
||||
android:layout_marginTop="10dp" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
@ -88,7 +67,9 @@
|
||||
android:id="@+id/editTextSrcFileName"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="/sdcard/Download/test.wav"
|
||||
android:layout_weight="1" />
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonSelectSrcFile"
|
||||
@ -111,6 +92,7 @@
|
||||
android:id="@+id/editTextOutFileName"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="/sdcard/Download/soundtouch-output.wav"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Button
|
||||
@ -120,8 +102,15 @@
|
||||
android:text="Select" />
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBoxPlay"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="Play the output file after processing!" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button1"
|
||||
android:id="@+id/buttonProcess"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
@ -130,14 +119,22 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="20dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="Status console:"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewResult"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="@string/hello_world" />
|
||||
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -1,21 +1,50 @@
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Example Android Application/Activity that allows processing WAV
|
||||
/// audio files with SoundTouch library
|
||||
///
|
||||
/// Copyright (c) Olli Parviainen
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $Id: SoundTouch.java 210 2015-05-14 20:03:56Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
package net.surina;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.surina.soundtouch.SoundTouch;
|
||||
import net.surina.soundtouchexample.R;
|
||||
import net.surina.soundtouchexample.R.id;
|
||||
import net.surina.soundtouchexample.R.layout;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class ExampleActivity extends Activity
|
||||
public class ExampleActivity extends Activity implements OnClickListener
|
||||
{
|
||||
TextView textViewConsole;
|
||||
TextView textViewConsole = null;
|
||||
EditText editSourceFile = null;
|
||||
EditText editOutputFile = null;
|
||||
EditText editTempo = null;
|
||||
EditText editPitch = null;
|
||||
CheckBox checkBoxPlay = null;
|
||||
|
||||
StringBuilder consoleText = new StringBuilder();
|
||||
|
||||
|
||||
/// Called when the activity is created
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
@ -23,38 +52,164 @@ public class ExampleActivity extends Activity
|
||||
setContentView(R.layout.activity_example);
|
||||
|
||||
textViewConsole = (TextView)findViewById(R.id.textViewResult);
|
||||
editSourceFile = (EditText)findViewById(R.id.editTextSrcFileName);
|
||||
editOutputFile = (EditText)findViewById(R.id.editTextOutFileName);
|
||||
|
||||
editTempo = (EditText)findViewById(R.id.editTextTempo);
|
||||
editPitch = (EditText)findViewById(R.id.editTextPitch);
|
||||
|
||||
// Check soundtouch
|
||||
Button buttonFileSrc = (Button)findViewById(R.id.buttonSelectSrcFile);
|
||||
Button buttonFileOutput = (Button)findViewById(R.id.buttonSelectOutFile);
|
||||
Button buttonProcess = (Button)findViewById(R.id.buttonProcess);
|
||||
buttonFileSrc.setOnClickListener(this);
|
||||
buttonFileOutput.setOnClickListener(this);
|
||||
buttonProcess.setOnClickListener(this);
|
||||
|
||||
checkBoxPlay = (CheckBox)findViewById(R.id.checkBoxPlay);
|
||||
|
||||
// Check soundtouch library presence & version
|
||||
checkLibVersion();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
|
||||
/// Function to append status text onto "console box" on the Activity
|
||||
public void appendToConsole(final String text)
|
||||
{
|
||||
textViewConsole = null;
|
||||
// run on UI thread to avoid conflicts
|
||||
runOnUiThread(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
consoleText.append(text);
|
||||
consoleText.append("\n");
|
||||
textViewConsole.setText(consoleText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/// Append text to console on the activity
|
||||
public void appendToConsole(String text)
|
||||
{
|
||||
if (textViewConsole == null) return;
|
||||
consoleText.append(text);
|
||||
consoleText.append("\n");
|
||||
textViewConsole.setText(consoleText);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// print SoundTouch native library version onto console
|
||||
protected void checkLibVersion()
|
||||
{
|
||||
SoundTouch st = new SoundTouch();
|
||||
|
||||
String ver = st.getVersionString();
|
||||
String ver = SoundTouch.getVersionString();
|
||||
appendToConsole("SoundTouch native library version = " + ver);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Button click handler
|
||||
@Override
|
||||
public void onClick(View arg0)
|
||||
{
|
||||
switch (arg0.getId())
|
||||
{
|
||||
case R.id.buttonSelectSrcFile:
|
||||
case R.id.buttonSelectOutFile:
|
||||
// one of the file select buttons clicked ... we've not just implemented them ;-)
|
||||
Toast.makeText(this, "File selector not implemented, sorry! Enter the file path manually ;-)", Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
|
||||
case R.id.buttonProcess:
|
||||
// button "process" pushed
|
||||
process();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Play audio file
|
||||
protected void playWavFile(String fileName)
|
||||
{
|
||||
File file2play = new File(fileName);
|
||||
Intent i = new Intent();
|
||||
i.setAction(android.content.Intent.ACTION_VIEW);
|
||||
i.setDataAndType(Uri.fromFile(file2play), "audio/wav");
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Helper class that will execute the SoundTouch processing. As the processing may take
|
||||
/// some time, run it in background thread to avoid hanging of the UI.
|
||||
protected class ProcessTask extends AsyncTask<ProcessTask.Parameters, Integer, Long>
|
||||
{
|
||||
/// Helper class to store the SoundTouch file processing parameters
|
||||
public final class Parameters
|
||||
{
|
||||
String inFileName;
|
||||
String outFileName;
|
||||
float tempo;
|
||||
float pitch;
|
||||
}
|
||||
|
||||
|
||||
/// Processing routine
|
||||
@Override
|
||||
protected Long doInBackground(Parameters... aparams)
|
||||
{
|
||||
Parameters params = aparams[0];
|
||||
|
||||
SoundTouch st = new SoundTouch();
|
||||
st.setTempo(params.tempo);
|
||||
st.setPitchSemiTones(params.pitch);
|
||||
Log.i("SoundTouch", "process file " + params.inFileName);
|
||||
long startTime = System.currentTimeMillis();
|
||||
int res = st.processFile(params.inFileName, params.outFileName);
|
||||
long endTime = System.currentTimeMillis();
|
||||
float duration = (endTime - startTime) * 0.001f;
|
||||
|
||||
Log.i("SoundTouch", "process file done, duration = " + duration);
|
||||
appendToConsole("Processing done, duration " + duration + " sec.");
|
||||
if (res != 0)
|
||||
{
|
||||
String err = SoundTouch.getErrorString();
|
||||
appendToConsole("Failure: " + err);
|
||||
return -1L;
|
||||
}
|
||||
|
||||
// Play file if so is desirable
|
||||
if (checkBoxPlay.isChecked())
|
||||
{
|
||||
playWavFile(params.outFileName);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// process a file with SoundTouch. Do the processing using a background processing
|
||||
/// task to avoid hanging of the UI
|
||||
protected void process()
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessTask task = new ProcessTask();
|
||||
ProcessTask.Parameters params = task.new Parameters();
|
||||
// parse processing parameters
|
||||
params.inFileName = editSourceFile.getText().toString();
|
||||
params.outFileName = editOutputFile.getText().toString();
|
||||
params.tempo = 0.01f * Float.parseFloat(editTempo.getText().toString());
|
||||
params.pitch = Float.parseFloat(editPitch.getText().toString());
|
||||
|
||||
// update UI about status
|
||||
appendToConsole("Process audio file :" + params.inFileName +" => " + params.outFileName);
|
||||
appendToConsole("Tempo = " + params.tempo);
|
||||
appendToConsole("Pitch adjust = " + params.pitch);
|
||||
|
||||
Toast.makeText(this, "Starting to process file " + params.inFileName + "...", Toast.LENGTH_SHORT).show();
|
||||
|
||||
// start processing task in background
|
||||
task.execute(params);
|
||||
|
||||
}
|
||||
catch (Exception exp)
|
||||
{
|
||||
exp.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -20,7 +20,61 @@ public final class SoundTouch
|
||||
// Native interface function that returns SoundTouch version string.
|
||||
// This invokes the native c++ routine defined in "soundtouch-jni.cpp".
|
||||
public native final static String getVersionString();
|
||||
|
||||
private native final void setTempo(long handle, float tempo);
|
||||
|
||||
private native final void setPitchSemiTones(long handle, float pitch);
|
||||
|
||||
private native final void setSpeed(long handle, float speed);
|
||||
|
||||
private native final int processFile(long handle, String inputFile, String outputFile);
|
||||
|
||||
public native final static String getErrorString();
|
||||
|
||||
private native final static long newInstance();
|
||||
|
||||
private native final void deleteInstance(long handle);
|
||||
|
||||
long handle = 0;
|
||||
|
||||
|
||||
public SoundTouch()
|
||||
{
|
||||
handle = newInstance();
|
||||
}
|
||||
|
||||
|
||||
public void close()
|
||||
{
|
||||
deleteInstance(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
||||
public void setTempo(float tempo)
|
||||
{
|
||||
setTempo(handle, tempo);
|
||||
}
|
||||
|
||||
|
||||
public void setPitchSemiTones(float pitch)
|
||||
{
|
||||
setPitchSemiTones(handle, pitch);
|
||||
}
|
||||
|
||||
|
||||
public void setSpeed(float speed)
|
||||
{
|
||||
setSpeed(handle, speed);
|
||||
}
|
||||
|
||||
|
||||
public int processFile(String inputFile, String outputFile)
|
||||
{
|
||||
return processFile(handle, inputFile, outputFile);
|
||||
}
|
||||
|
||||
|
||||
// Load the native library upon startup
|
||||
static
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user