Android – Ndk-Build

music

Cześć dzisiaj pokażę Ci jak możesz do swoich Androidowych projektów wykorzystać biblioteke mp3Lame.


 
 
 
 
 
 

Mp3Lame

 

Ostatnio pochwaliłem się projektem mixera audio i na jego podstawie miałem pokazać Ci niektóre patenty które możesz wykorzystać w swoich aplikacjach. W swoim projekcie wykorzystałem bibliotekę mp3lame napisaną w natywnych językach z rodziny C. Gdy pisałem moją aplikację nie było możliwości wykorzystania CMake co znacznie ułatwia sprawę po prostu wybierasz czy twój projekt ma zawierać natywne kody i działasz (chociaż nie wiem jakby było z dodaniem biblioteki) ja poszedłem inną drogą będziemy robić dokładnie to.

 

 

Let’s do it

 
Na początek należy zainstalować ndk  Tools > Android > SDK Manager i instalujemy NDK. Teraz utwórz sobie pakiet jni tak jak niżej.
 

 

Teraz pobieramy Mp3Lame. Do nowo utworzonego folderu kopiujemy folder libmp3lame z pobranego zipa, dokładnie interesują nas tylko pliki z rozszerzeniem *.h, *.c. Teraz tworzymy dwa pliki Application.mk który powinien wyglądać tak:

 

APP_PLATFORM := android-11
APP_ABI := arm64-v8a armeabi-v7a x86 x86_64
NDK_TOOLCHAIN_VERSION := clang
APP_OPTIM := release

 

 oraz Android.mk:

 

LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)

    LOCAL_MODULE    	:= libmp3lame
    LOCAL_SRC_FILES 	:= \
    ./libmp3lame/bitstream.c \
    ./libmp3lame/encoder.c \
    ./libmp3lame/fft.c \
    ./libmp3lame/gain_analysis.c \
    ./libmp3lame/id3tag.c \
    ./libmp3lame/lame.c \
    ./libmp3lame/mpglib_interface.c \
    ./libmp3lame/newmdct.c \
    ./libmp3lame/presets.c \
    ./libmp3lame/psymodel.c \
    ./libmp3lame/quantize.c \
    ./libmp3lame/quantize_pvt.c \
    ./libmp3lame/reservoir.c \
    ./libmp3lame/set_get.c \
    ./libmp3lame/tables.c \
    ./libmp3lame/takehiro.c \
    ./libmp3lame/util.c \
    ./libmp3lame/vbrquantize.c \
    ./libmp3lame/VbrTag.c \
    ./libmp3lame/version.c \
    ./wrapper.c

    LOCAL_LDLIBS := -llog
    LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
    LOCAL_MULTILIB := 32

    include $(BUILD_SHARED_LIBRARY)

 
Nasza konfiguracja zrobiona teraz możesz skompilować bibliotekę w tym celu otwieramy folder z ndk (ścieżka do sdk).
 

 

Kompilacja

 

ndk-build - C ścieżkaDoNaszegoKataloguJni

 

Napewno będziesz miał teraz dużo błędów na które poprawki możesz znaleźć na stackOverFlow, ja nie będe teraz pisał jak je poprawiać bo nie chce mi się szukać:) mam wersję folderu “libMp3Lame” który kiedyś doprowadziłem do porządku i działa, nie martw się udostępnie Ci go na githubie:). Po udanej kompilacji powinieneś mieć taki output a także powinny utworzyć się dwa nowe foldery (obj, libs) ze skompilowanymi źródłami.

 

[x86_64] Compile        : mp3lame <= version.c
[x86_64] Compile        : mp3lame <= wrapper.c
[x86_64] SharedLibrary  : libmp3lame.so
[x86_64] Install        : libmp3lame.so => libs/x86_64/libmp3lame.so

 

Kody

 

Teraz możemy napisać sobie Wrapper czyli klasę do obsługi biblioteki. Do moich celów wystarczyła metoda która konwertuje plik *.wav do mp3. Zauważ specyficzne nazwy metod. JAVA_nazwaDomeny_nazwaPakietu_nazwaKlasa_nazwaMetody().

 

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#include <android/log.h>
#include "libmp3lame/lame.h"

#define LOG_TAG "LAME ENCODER"
#define LOGD(format, args...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, format, ##args);
#define BUFFER_SIZE 8192
#define be_short(s) ((short) ((unsigned short) (s) << 8) | ((unsigned short) (s) >> 8))

lame_t lame;

int read_samples(FILE *input_file, short *input) {
  int nb_read;
  nb_read = fread(input, 1, sizeof(short), input_file) / sizeof(short);

  int i = 0;
  while (i < nb_read) {
    input[i] = be_short(input[i]);
    i++;
  }

  return nb_read;
}

void Java_info_gert_mp3lameAndroid_Mp3Encoder_initEncoder(JNIEnv *env,
    jobject jobj, jint in_num_channels, jint in_samplerate, jint in_brate,
    jint in_mode, jint in_quality) {
  lame = lame_init();

//	LOGD("Init parameters:");
  lame_set_num_channels(lame, in_num_channels);
//	LOGD("Number of channels: %d", in_num_channels);
  lame_set_in_samplerate(lame, in_samplerate);
//	LOGD("Sample rate: %d", in_samplerate);
  lame_set_brate(lame, in_brate);
//	LOGD("Bitrate: %d", in_brate);
  lame_set_mode(lame, in_mode);
//	LOGD("Mode: %d", in_mode);
  lame_set_quality(lame, in_quality);
//	LOGD("Quality: %d", in_quality);

  int res = lame_init_params(lame);
//	LOGD("Init returned: %d", res);
}

void Java_info_gert_mp3lameAndroid_Mp3Encoder_destroyEncoder(
    JNIEnv *env, jobject jobj) {
  int res = lame_close(lame);
//	LOGD("Deinit returned: %d", res);
}

int Java_info_gert_mp3lameAndroid_Mp3Encoder_encode(JNIEnv *env, jobject jobj, jstring in_source_path, jstring in_target_path){

const char *source_path, *target_path;
source_path = (*env)->GetStringUTFChars(env, in_source_path, NULL);
target_path = (*env)->GetStringUTFChars(env, in_target_path, NULL);

int read, write;

    FILE *pcm = fopen(source_path, "rb");
    FILE *mp3 = fopen(target_path, "wb");

    const int PCM_SIZE = 8192;
    const int MP3_SIZE = 8192;

    short int pcm_buffer[PCM_SIZE*2];
    unsigned char mp3_buffer[MP3_SIZE];

    lame_t lame = lame_init();
    lame_set_in_samplerate(lame, 44100);
    lame_set_VBR(lame, vbr_off);
    lame_init_params(lame);

    do {
        read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
        if (read == 0)
            write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
        else
            write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
        fwrite(mp3_buffer, write, 1, mp3);
    } while (read != 0);

    lame_close(lame);
    fclose(mp3);
    fclose(pcm);

    return 0;
}
 

 
Gdy już mamy gotowy Wrapper znów musimy skompilować bibliotekę. Teraz, aby wykorzystać naszą bibliotekę w aplikacji. Najlepiej utworzyć sobie nową klasę przez którą będziemy wywoływać interesujące nas metody.
 

@SuppressWarnings("JniMissingFunction")
public class Mp3Encoder {

    static {
        System.loadLibrary("mp3lame");
    }
    public native int encode(String sourcePath, String targetPath);
}

 
Najpierw ładujemy biblioteke i wywołujemy nasze metody ze specyficznym słowem native. Aby system znalazł naszą bibliotekę tworzymy jeszcze folder jniLibs.
 

 
Do jniLibs kopiujemy zawartość nowo utworzonego przy kompilacji folderu libs. To tyle przykładowy program wystawiam Ci na githubie

Cześć.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *