Android NDK Development (6)-use open-source LAME to transcode mp3 and androidndk

Source: Internet
Author: User

Android NDK Development (6)-use open-source LAME to transcode mp3 and androidndk

Reprinted please indicate the source:Http://blog.csdn.net/allen315410/article/details/42456661

In the previous blogs in this column, I talked about some Android NDK development basics, from environment setup to using JNI for mutual calls of Java and C code, in addition, the Demo is simple and easy to understand. I believe that if I have mastered most of the content of the previous blog, I can start to use JNI for NDK development in the actual project. Now that the basics have passed, next I will try to use real projects here. We know that C language is highly efficient and one of the earliest advanced programming languages. It has been alive for nearly 40 years, so many efficient class libraries developed using C can be reused, this not only achieves high efficiency, but also reduces the project development cycle. Here I found an LAME, the most common class library for audio file transcoding, which is a small project for developing a wav audio file to be transcoded into an mp3 audio file based on LAME.


I. LAME Introduction

LAME is currently the best MP3 encoding engine. The MP3 encoded by LAME features pure tone, wide space, clear bass, and sound details. Its original audio Model Technology ensures the authenticity of CD audio restoration, and works with VBR and ABR parameters, sound quality is almost comparable to CD audio, but the file size is very small. For a free engine, the advantage of LAME is self-evident. The LAME introduction can be found in Baidu encyclopedia and Wikipedia. I will not go into details here, however, we need to know that LAME can help us to transcode wav lossless audio files to mp3, a relatively small audio format file.

Note: wav and mp3 are a series of audio codec algorithms. I am not particularly clear about their advantages and implementation principles, if you want to learn more about audio encoding, you still need to find some relevant documents to read, but it is unclear that this does not prevent us from developing such a project.

The LAME source code is hosted on sourceforge.net. When we develop an LAME-based project, we have to download its source code for compilation.

LAME home page: http://lame.sourceforge.net/

LAME download: http://sourceforge.net/projects/lame/files/lame/3.99/

Currently the latest version of LAME is 3.99.5, can not be connected to the SourceForge home page friends can click to download csdn, I have uploaded, address is: http://download.csdn.net/detail/lee_tianya/8332357


2. Compile local methods

When developing this transcoding project, let's take a simple look and write two simple Native Methods: convertmp3 (String wav, String mp3), which is the main Native method, the purpose is to pass the paths of the two audio files obtained from the Java layer to the C-end in string form. The C-end can get these two paths for read/write and CODEC operations, another Native method is getLameVersion (). This method obtains the LAME version number and returns a string. It is used to verify whether the local compilation is successful. so file. The source code is as follows:

/*** Local method for converting wav to mp3 ** @ param wav * @ param mp3 */public native void convertmp3 (String wav, String mp3 ); /*** get the LAME version information ** @ return */public native String getLameVersion ();

3. Compile the header file

Through the previous blogs, we know that after defining the Native method we need in the Java layer, we need to use the javah Command provided by JDK to compile the method signature required in the C file, the specific method is 1. If you are using JDK 1.6 or earlier, switch the directory to the classes directory under the project directory; 2. If you are using JDK 1.7, switch the directory to the src directory under the project directory and execute the following javah command :,


Good! If no error is reported, the method signature file is generated. Find the method signature file under the src directory and create a jni directory under the project directory, copy the method signature file to the jni directory. open the file and check the content.

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_example_lame_MainActivity */#ifndef _Included_com_example_lame_MainActivity#define _Included_com_example_lame_MainActivity#ifdef __cplusplusextern "C" {#endif/* * Class:     com_example_lame_MainActivity * Method:    convertmp3 * Signature: (Ljava/lang/String;Ljava/lang/String;)V */JNIEXPORT void JNICALL Java_com_example_lame_MainActivity_convertmp3  (JNIEnv *, jobject, jstring, jstring);/* * Class:     com_example_lame_MainActivity * Method:    getLameVersion * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_lame_MainActivity_getLameVersion  (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif

4. Import the LAME source code to the jni directory

The project should be particularly important and careful when it comes to this process, because the open-source library LAME needs to be referenced in this project and re-compiled into a library suitable for this platform.

1. decompress the downloaded LAME source code to a local directory, open the decompressed directory, find the libmp 3lame directory, and copy all the files under the directory to the jni directory.

2. Remove unnecessary file directories. For example, if you want to delete the i386 directory, You need to delete several non-. h,. c files as extension files that have been processed in Linux, because these files are not necessary on the Android platform.

3. Introduce the lame. h header file. Find the include directory under the LAME decompression directory and copy the lame. h header file under it to the jni directory. If this directory is not introduced, the following error will be reported:


4. Modify the source code of util. h. Locate util in the jni directory. h file, locate the ieee754_float32_t data type in row 574, and change it to the float type, because ieee754_float32_t is a data type supported in Linux or Unix, which is not supported in Android. If no modification is made, the following error will be reported during source code compilation:



5. Write local C language code to transcode audio files

It is indeed difficult to compile this C language code. It requires a certain degree of knowledge in C language and must be able to understand lame. the APIs and comments provided in the H file are not described here, because it is much more complicated to say, and a blog is incomplete, the C language code is posted below. The Code provides more detailed comments and you can refer to the Code with comments for ease of understanding!

# Include <stdio. h> # include <jni. h> # include <malloc. h> # include <string. h> # include <lame. h> # include "com_example_lame_MainActivity.h" # include <android/log. h> # define LOG_TAG "System. out. c "# define LOGD (...) _ android_log_print (ANDROID_LOG_DEBUG, LOG_TAG, _ VA_ARGS _) # define LOGI (...) _ android_log_print (ANDROID_LOG_INFO, LOG_TAG, _ VA_ARGS _)/*** return value char * indicates the first address of the char array * Jstring2CStr converts the jstring type in java into Char string */char * Jstring2CStr (JNIEnv * env, jstring jstr) {char * rtn = NULL; jclass clsstring = (* env)-> FindClass (env, "java/lang/String"); // Stringjstring strencode = (* env)-> NewStringUTF (env, "GB2312 "); // obtain a java String "GB2312" jmethodID mid = (* env)-> GetMethodID (env, clsstring, "getBytes", "(Ljava/lang/String ;) [B "); // [String. getBytes ("gb2312"); jbyteArray barr = (jbyteArray) (* env)-> CallObjectM Ethod (env, jstr, mid, strencode); // String. getByte ("GB2312"); jsize alen = (* env)-> GetArrayLength (env, barr); // The length of the byte array jbyte * ba = (* env) -> GetByteArrayElements (env, barr, JNI_FALSE); if (alen> 0) {rtn = (char *) malloc (alen + 1 ); // "\ 0" memcpy (rtn, ba, alen); rtn [alen] = 0;} (* env)-> ReleaseByteArrayElements (env, barr, ba, 0 ); // return rtn;} int flag = 0;/*** convert mp3 */JNIEXPORT void JNICALL Java_c Convert (JNIEnv * env, jobject obj, jstring jwav, jstring jmp3) {char * cwav = Jstring2CStr (env, jwav); char * cmp3 = Jstring2CStr (env, jmp3 ); LOGI ("wav = % s", cwav); LOGI ("mp3 = % s", cmp3); // 1. open wav, MP3 FILE * fwav = fopen (cwav, "rb"); FILE * fmp3 = fopen (cmp3, "wb"); short int wav_buffer [8192*2]; unsigned char mp3_buffer [8192]; // 1. encoder lame_t lame = lame_init (); // 2. set lame Mp3 encoded sampling rate lame_set_in_samplerate (lame, 44100); lame_set_num_channels (lame, 2); // 3. set the MP3 encoding method lame_set_VBR (lame, vbr_default); lame_init_params (lame); LOGI ("lame init finish"); int read; int write; // It indicates the number of reads and writes. int total = 0; // The number of bytes in the currently read wav file do {if (flag = 404) {return;} read = fread (wav_buffer, sizeof (short int) * 2, 8192, fwav); total + = read * sizeof (short int) * 2; LOGI ("converting .... % d ", total); publish JavaProgress (env, obj, total); // call the java code to update the progress bar if (read! = 0) {write = lame_encode_buffer_interleaved (lame, wav_buffer, read, mp3_buffer, 8192); // write the converted mp3 data to the file fwrite (mp3_buffer, sizeof (unsigned char ), write, fmp3) ;}if (read = 0) {lame_encode_flush (lame, mp3_buffer, 8192) ;}} while (read! = 0); LOGI ("convert finish"); lame_close (lame); fclose (fwav); fclose (fmp3 );} /*** call java code update progress bar */void publishJavaProgress (JNIEnv * env, jobject obj, jint progress) {// 1. find the java MainActivity classjclass clazz = (* env)-> FindClass (env, "com/example/lame/MainActivity"); if (clazz = 0) {LOGI ("can't find clazz");} LOGI ("find clazz"); // 2 find the method in the class and define jmethodID methodid = (* env) -> GetMethodID (env, clazz, "setConvertProgress", "(I) V"); if (methodid = 0) {LOGI ("can't find methodid ");} LOGI ("find methodid"); // 3. call method (* env)-> CallVoidMethod (env, obj, methodid, progress);}/*** get the LAME version number */JNIEXPORT jstring JNICALL extension (JNIEnv * env, jobject obj) {return (* env)-> NewStringUTF (env, get_lame_version ());}
A convert is created above. c source file. The source code first references some related class libraries for use, such as <stdio. h> <jni. h> <malloc. h> <string. h> these are the standard class libraries of C, which are introduced as needed in the code. Next, we need to reference the compiled method signature file com_example_lame_MainActivity.h in the third topic. This file defines the signature of the Native method, so that the compiler can find the corresponding Native method, there is also a very important header file lame. h. This project uses LAME, so this lame. the header file of h must be referenced, and the last file is log. the introduction and definition of h are both fixed content. Just look at the source code.


6. Write configuration files and cross-Compilation

After the above steps and the source code are compiled, the remaining configuration file is used. First, let's take a look at Android. mk file, this is troublesome, because above we copied all the LAME source code files to the jni directory, these files are to be re-compiled, so we need to go to Android. in the LOCAL_SRC_FILES field of the mk file, configure the source code files, including not only the custom c file, but also the c source code file in the LAME file, because the LAME contains too many files, you must be extremely careful when writing LOCAL_SRC_FILES. If you write an error, the encoding may fail. The following is my configuration scheme:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := convertLOCAL_SRC_FILES := convert.c bitstream.c fft.c id3tag.c mpglib_interface.c presets.c  quantize.c   reservoir.c tables.c  util.c  VbrTag.c encoder.c  gain_analysis.c lame.c  newmdct.c   psymodel.c quantize_pvt.c set_get.c  takehiro.c vbrquantize.c version.cLOCAL_LDLIBS += -lloginclude $(BUILD_SHARED_LIBRARY)
Application. mk

APP_PLATFORM := android-8
Cross-compilation in cygwin:

Good! The prompt at the bottom shows our description. so is compiled, because there are many files, it takes a little time to compile, and the above error reported a lot of warning warnings, but it does not matter, you can ignore, as long as it is not an error, the project can still run.


7. Compile the code for calling the C-end at the Java Layer

Before writing the call code in Java, we consider that codec is a very time-consuming operation, so this operation cannot be executed in the main thread, and new threads must be enabled, this operation is time-consuming, so for a good user experience, we need to add a progress bar dialog box during transcoding to prompt a transcoding process, the progress of this progress bar is also implemented in the C language. In Java, we only need to define a method for setting the progress, and call back this java method in the C language, pass the progress data to the progress bar dialog box in Java.

Public class MainActivity extends Activity {static {System. loadLibrary ("convert");} private EditText et_wav; private EditText et_mp3; private ProgressDialog pd; /*** local method for converting wav to mp3 ** @ param wav * @ param mp3 */public native void convertmp3 (String wav, String mp3 ); /*** get the LAME version information ** @ return */public native String getLameVersion (); @ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); et_wav = (EditText) this. findViewById (R. id. et_wav); et_mp3 = (EditText) this. findViewById (R. id. et_mp3); pd = new ProgressDialog (this);}/*** wav conversion mp3 */public void convert (View view) {final String mp3Path = et_mp3.getText (). toString (). trim (); final String wavPath = et_wav.getText (). toString (). trim (); File file = new File (wavPath); int size = (int) file. length (); System. out. println ("file size" + size); if ("". equals (mp3Path) | "". equals (wavPath) {Toast. makeText (MainActivity. this, "path cannot be blank", 1 ). show (); return;} pd. setMessage ("converting .... "); pd. setProgressStyle (ProgressDialog. STYLE_HORIZONTAL); pd. setMax (size); // set the maximum value of the progress bar pd. setCancelable (false); pd. show (); // transcoding is a time-consuming operation, so you need to enable a new Thread to execute new Thread () {@ Overridepublic void run () {convertmp3 (wavPath, mp3Path ); pd. dismiss ();}}. start ();}/*** sets the progress of the progress bar and provides it to the C language to call ** @ param progress */public void setConvertProgress (int progress) {pd. setProgress (progress);}/*** get the LAME version */public void getVersion (View view) {Toast. makeText (MainActivity. this, getLameVersion (), 0 ). show ();}}
Note that you need to add two permissions:

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
8. Test Well. After writing it here, a project is finished. Next we will perform a test. First we will open an arm Simulator and then run the project.

1. Test version number


2. Convert wav to mp3

Okay. Now let's take a look at the original wav audio files and the transcoded mp3 files under sdcard.

We can see that our transcoding is usable. The original wav file is indeed transcoded into an mp3 file, and the size is only 1/10 of the size of the original file. Export the mp3 file and play it with the audio player, the file is not damaged. How is it? After the project is finished, are you interested in trying?


Download the source code here



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.