Skip to content

Commit

Permalink
Example. Works fine without main methods
Browse files Browse the repository at this point in the history
  • Loading branch information
lincollincol committed Aug 26, 2020
1 parent 9923c49 commit 2f99573
Show file tree
Hide file tree
Showing 31 changed files with 450 additions and 89 deletions.
1 change: 0 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,4 @@ android {

dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
}
11 changes: 1 addition & 10 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="linc.com.amplituda">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:extractNativeLibs="false"/>
</manifest>
<manifest package="linc.com.amplituda"/>
106 changes: 34 additions & 72 deletions app/src/main/cpp/native-lib.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
#include <jni.h>
#include <string>

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}

std::string ConvertJString(JNIEnv* env, jstring str){
if ( !str ) std::string();
const jsize len = env->GetStringUTFLength(str);
const char* strChars = env->GetStringUTFChars(str, (jboolean *)0);
std::string Result(strChars, len);
env->ReleaseStringUTFChars(str, strChars);
return Result;
}

float getSample(const AVCodecContext* codecCtx, uint8_t* buffer, int sampleIndex) {
int64_t val = 0;
float ret = 0;
Expand All @@ -15,19 +25,12 @@ float getSample(const AVCodecContext* codecCtx, uint8_t* buffer, int sampleIndex
val = (reinterpret_cast<uint8_t*>(buffer))[sampleIndex];
val -= 127;
break;

case 2:
val = (reinterpret_cast<uint16_t*>(buffer))[sampleIndex];
case 2: val = (reinterpret_cast<uint16_t*>(buffer))[sampleIndex];
break;

case 4:
val = (reinterpret_cast<uint32_t*>(buffer))[sampleIndex];
case 4: val = (reinterpret_cast<uint32_t*>(buffer))[sampleIndex];
break;

case 8:
val = (reinterpret_cast<uint64_t*>(buffer))[sampleIndex];
case 8: val = (reinterpret_cast<uint64_t*>(buffer))[sampleIndex];
break;

default:
return 0;
}
Expand All @@ -43,21 +46,17 @@ float getSample(const AVCodecContext* codecCtx, uint8_t* buffer, int sampleIndex
// integer => Scale to [-1, 1] and convert to float.
ret = val / (static_cast<float>(((1 << (sampleSize*8-1))-1)));
break;

case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
// float => reinterpret
ret = *reinterpret_cast<float*>(&val);
break;

case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_DBLP:
// double => reinterpret and then static cast down
ret = static_cast<float>(*reinterpret_cast<double*>(&val));
break;

default:
// fprintf(stderr, "Invalid sample format %s.\n", av_get_sample_fmt_name(codecCtx->sample_fmt));
return 0;
}

Expand All @@ -69,93 +68,61 @@ extern "C" JNIEXPORT jstring JNICALL

Java_linc_com_amplituda_Amplituda_stringFromJNI(
JNIEnv* env,
jobject) {
// You can change the filename for any other filename/supported format
// const char *filename = "../my file.ogg";
jobject,
jstring audio_path
) {

// std::string resultFrame = "";
std::string filename = ConvertJString( env, audio_path );
std::string resultFrame;

// SUPPORTED AUDIO FORMATS
// [mp3, opus, oga, ogg, m4a, mp4] / 1h audio processing = 30sec

// const char *filename = "/storage/emulated/0/viber/kygo.mp3";
// const char *filename = "/storage/emulated/0/viber/ex.wav";
const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/music/f_voip_dur.opus";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/music/Голос 0017325996153317080688.m4a";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/video_notes/5406735884265457666.mp4";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/voice/5382102159468791418.oga";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/music/DiscDj_Rec_2020-06-21_18-38-44.mp3";

// Initialize FFmpeg
av_register_all();

// AVFrame* frame = avcodec_alloc_frame();
AVFrame* frame = av_frame_alloc();

if (!frame)
{
// std::cout << "Error allocating the frame. Let's try again shall we?\n";
return env->NewStringUTF("666"); // fail at start: 66 = number of the beast
if (!frame){
return env->NewStringUTF("Error allocating the frame! Amplituda:exception:600");
}

// you can change the file name to whatever yo need:)
AVFormatContext* formatContext = NULL;
if (avformat_open_input(&formatContext, filename, NULL, NULL) != 0)
{
if (avformat_open_input(&formatContext, filename.data(), NULL, NULL) != 0) {
av_free(frame);
// std::cout << "Error opening file " << filename<< "\n";
return env->NewStringUTF("800"); // cant open file. 800 = Boo!
return env->NewStringUTF("Error opening file! Amplituda:exception:800");
}

if (avformat_find_stream_info(formatContext, NULL) < 0)
{
if (avformat_find_stream_info(formatContext, NULL) < 0){
av_free(frame);
avformat_close_input(&formatContext);
// std::cout << "Error finding the stream information.\nCheck your paths/connections and the details you supplied!\n";
return env->NewStringUTF("57005"); // stream info error. 0xDEAD in hex is 57005 in decimal
return env->NewStringUTF("Error finding the stream information! Amplituda:exception:57005");
}

// Find the audio stream
AVCodec* cdc = nullptr;
int streamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, &cdc, 0);
if (streamIndex < 0)
{
if (streamIndex < 0){
av_free(frame);
avformat_close_input(&formatContext);
// std::cout << "Could not find any audio stream in the file. Come on! I need data!\n";
return env->NewStringUTF("165"); // no(0) (a)udio s(5)tream: 0A5 in hex = 165 in decimal
return env->NewStringUTF("Could not find any audio stream in the file! Amplituda:exception:165");
}

AVStream* audioStream = formatContext->streams[streamIndex];
AVCodecContext* codecContext = audioStream->codec;
codecContext->codec = cdc;

if (avcodec_open2(codecContext, codecContext->codec, NULL) != 0)
{
if (avcodec_open2(codecContext, codecContext->codec, NULL) != 0){
av_free(frame);
avformat_close_input(&formatContext);
// std::cout << "Couldn't open the context with the decoder. I can decode but I need to have something to decode.\nAs I couldn't find anything I have surmised the decoded output is 0!\n (Well can't have you thinking I am doing nothing can we?\n";
return env->NewStringUTF("1057"); // cant find/open context 1057 = lost
return env->NewStringUTF("Couldn't open the context with the decoder! (can't find or open context) Amplituda:exception:1057");
}

// std::cout << "This stream has " << codecContext->channels << " channels with a sample rate of " << codecContext->sample_rate << "Hz\n";
// std::cout << "The data presented in format: " << av_get_sample_fmt_name(codecContext->sample_fmt) << std::endl;

AVPacket readingPacket;
av_init_packet(&readingPacket);

// Read the packets in a loop
while (av_read_frame(formatContext, &readingPacket) == 0)
{
if (readingPacket.stream_index == audioStream->index)
{
AVPacket decodingPacket = readingPacket;
// Audio packets can have multiple audio frames in a single packet
while (decodingPacket.size > 0)
{
// Try to decode the packet into a frame(s)
// Some frames rely on multiple packets, so we have to make sure the frame is finished
// before utilising it
int gotFrame = 0;
int result = avcodec_decode_audio4(codecContext, frame, &gotFrame, &decodingPacket);

Expand All @@ -165,23 +132,22 @@ Java_linc_com_amplituda_Amplituda_stringFromJNI(
decodingPacket.data += result;

float sum = 0;
// for(int s = 0; s < frame->nb_samples; ++s) {
for(int s = 0; s < 4; ++s) {
for(int c = 0; c < codecContext->channels; ++c) {
float sample = getSample(codecContext, frame->extended_data[c], s);
if(sample < 0)
sum += -sample;
else
sum += sample;
// __android_log_print(ANDROID_LOG_VERBOSE, "NDK_NDK_NDK", "First loop = %f", sample);
}
}
float average_point = (sum * 2) / 4;
// float average_point = (sum * 2) / 4;

int ampl_av = pow(((int)(average_point * 100)), 0.5);
// int amplitude = pow(((int)(average_point * 100)), 0.5);
int amplitude = pow(((int)(((sum * 2) / 4) * 100)), 0.5);

// resultFrame.append(std::to_string( ampl_av ));
// resultFrame.append("\n");
resultFrame.append(std::to_string( amplitude ));
resultFrame.append("\n");
}
else
{
Expand All @@ -191,15 +157,11 @@ Java_linc_com_amplituda_Amplituda_stringFromJNI(
}
}

// You MUST call av_free_packet() after each call to av_read_frame()
// or you will leak so much memory on a large file you will need a memory-plumber!
av_free_packet(&readingPacket);
}

// Clean up! (unless you have a quantum memory machine with infinite RAM....)
av_free(frame);
avcodec_close(codecContext);
avformat_close_input(&formatContext);
// return env->NewStringUTF(resultFrame.data()); // success!!!!!!!!
return env->NewStringUTF("0"); // success!!!!!!!!
return env->NewStringUTF(resultFrame.data());
}
33 changes: 30 additions & 3 deletions app/src/main/java/linc/com/amplituda/Amplituda.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,45 @@
package linc.com.amplituda;

import android.content.Context;

public class Amplituda {

static {
System.loadLibrary("native-lib");
}

public void init() {
System.out.println("AMPLITUDE ===================== " + stringFromJNI());
public void init(SuccessCallback successCallback) {
// System.out.println("AMPLITUDE ===================== " + stringFromJNI());
successCallback.onSuccess(stringFromJNI("/storage/emulated/0/viber/kygo.mp3"));
// const char *filename = "/storage/emulated/0/viber/kygo.mp3";
// const char *filename = "/storage/emulated/0/viber/ex.wav";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/music/f_voip_dur.opus";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/music/Голос 0017325996153317080688.m4a";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/video_notes/5406735884265457666.mp4";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/voice/5382102159468791418.oga";
// const char *filename = "/storage/emulated/0/Android/data/org.thunderdog.challegram/files/music/DiscDj_Rec_2020-06-21_18-38-44.mp3";
// SUPPORTED AUDIO FORMATS
// [mp3, opus, oga, ogg, m4a, mp4] / 1h audio processing = 30sec

}

public Amplituda withContext(Context context) {
return this;
}
public Amplituda fromFile() {
return this;
}
public Amplituda fromPath() {
return this;
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
native String stringFromJNI();
native String stringFromJNI(String pathToAudio);

public interface SuccessCallback {
void onSuccess(String log);
}

}
3 changes: 0 additions & 3 deletions app/src/main/res/values/strings.xml

This file was deleted.

1 change: 1 addition & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
34 changes: 34 additions & 0 deletions example/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
ndkVersion "21.3.6528147"

defaultConfig {
applicationId "linc.com.example"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation project(":app")
}
21 changes: 21 additions & 0 deletions example/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package linc.com.example;

import android.content.Context;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("linc.com.example", appContext.getPackageName());
}
}
Loading

0 comments on commit 2f99573

Please sign in to comment.