From 5dc023cd23373a8c5d390ddb2bda529597824f8b Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 04:41:30 +0900 Subject: [PATCH 01/31] Begin Status : basic structure for v0.8 > Remove kotlin code & dependency > Remove unnecessary folders(main/res, main/jni, main/libs, main/obj) > Made CMakeLists.txt from Android.mk > Reconstruct cpp management using CMakeLists.txt --- build.gradle | 16 +- euphony/build.gradle | 57 ++- euphony/src/main/AndroidManifest.xml | 4 +- euphony/src/main/cpp/CMakeLists.txt | 21 + .../main/{jni => cpp/arms}/_kiss_fft_guts.h | 0 euphony/src/main/{jni => cpp/arms}/kiss_fft.c | 0 euphony/src/main/{jni => cpp/arms}/kiss_fft.h | 0 .../src/main/{jni => cpp/arms}/kiss_fftr.c | 0 .../src/main/{jni => cpp/arms}/kiss_fftr.h | 0 euphony/src/main/cpp/native-legacy.cpp | 157 +++++++ .../euphony/rx}/AcousticSensor.java | 2 +- .../euphony/rx}/AudioRecorder.java | 3 +- .../euphony/rx}/EuDataDecoder.java | 5 +- .../euphony/rx}/EuFreqObject.java | 13 +- .../euphony/rx}/EuRxManager.java | 4 +- .../receiver => co/euphony/rx}/EuWindows.java | 2 +- .../receiver => co/euphony/rx}/KissFFT.java | 118 +++--- .../euphony/rx}/PositionDetector.java | 2 +- .../euphony/tx}/EuCodeMaker.java | 4 +- .../euphony/tx}/EuDataEncoder.java | 9 +- .../euphony/tx}/EuFreqGenerator.java | 4 +- .../euphony/tx}/EuPlayer.java | 4 +- .../euphony/tx}/EuTxManager.java | 4 +- .../lib => co/euphony}/util/COMMON.java | 2 +- .../lib => co/euphony}/util/ErrorHandler.java | 2 +- .../lib => co/euphony}/util/EuCodec.java | 2 +- .../lib => co/euphony}/util/EuOption.java | 2 +- .../euphony}/util/PacketErrorDetector.java | 2 +- .../euphony/lib/util/EuAfRangeFinder.java | 127 ------ euphony/src/main/jni/Android.mk | 74 ---- euphony/src/main/jni/Application.mk | 1 - .../src/main/jni/com_euphony_fft_KissFFT.h | 53 --- .../main/jni/euphony_lib_receiver_KissFFT.cpp | 132 ------ .../main/jni/euphony_lib_receiver_KissFFT.h | 61 --- .../euphony/lib/receiver/AcousticSensor.kt | 5 - .../euphony/lib/receiver/AudioRecorder.kt | 87 ---- .../euphony/lib/receiver/EuDataDecoder.kt | 42 -- .../euphony/lib/receiver/EuFreqObject.kt | 393 ------------------ .../euphony/lib/receiver/EuRxManager.kt | 208 --------- .../kotlin/euphony/lib/receiver/EuWindows.kt | 103 ----- .../kotlin/euphony/lib/receiver/KissFFT.kt | 117 ------ .../euphony/lib/receiver/PositionDetector.kt | 6 - .../euphony/lib/transmitter/EuCodeMaker.kt | 71 ---- .../euphony/lib/transmitter/EuDataEncoder.kt | 63 --- .../lib/transmitter/EuFreqGenerator.kt | 134 ------ .../euphony/lib/transmitter/EuPlayer.kt | 38 -- .../euphony/lib/transmitter/EuTxManager.kt | 63 --- .../main/kotlin/euphony/lib/util/COMMON.kt | 23 - .../main/kotlin/euphony/lib/util/EuCodec.kt | 32 -- .../euphony/lib/util/PacketErrorDetector.kt | 136 ------ euphony/src/main/libs/arm64-v8a/libkissff.so | Bin 10144 -> 0 bytes euphony/src/main/libs/arm64-v8a/libkissfft.so | Bin 10080 -> 0 bytes .../src/main/libs/arm64-v8a/libkissfftr.so | Bin 10080 -> 0 bytes .../src/main/libs/armeabi-v7a/libkissff.so | Bin 22188 -> 0 bytes .../src/main/libs/armeabi-v7a/libkissfft.so | Bin 18092 -> 0 bytes .../src/main/libs/armeabi-v7a/libkissfftr.so | Bin 18092 -> 0 bytes .../src/main/obj/local/arm64-v8a/libkissff.so | Bin 24824 -> 0 bytes .../main/obj/local/arm64-v8a/libkissfft.so | Bin 57208 -> 0 bytes .../main/obj/local/arm64-v8a/libkissfftr.so | Bin 17936 -> 0 bytes .../src/main/obj/local/arm64-v8a/libstdc++.a | 1 - .../local/arm64-v8a/objs/kissff/kiss_fft.o | Bin 36632 -> 0 bytes .../local/arm64-v8a/objs/kissff/kiss_fft.o.d | 8 - .../kissfft/euphony_lib_receiver_KissFFT.o | Bin 78832 -> 0 bytes .../kissfft/euphony_lib_receiver_KissFFT.o.d | 11 - .../local/arm64-v8a/objs/kissfftr/kiss_fftr.o | Bin 15976 -> 0 bytes .../arm64-v8a/objs/kissfftr/kiss_fftr.o.d | 11 - .../main/obj/local/armeabi-v7a/libkissff.so | Bin 112920 -> 0 bytes .../main/obj/local/armeabi-v7a/libkissfft.so | Bin 148316 -> 0 bytes .../main/obj/local/armeabi-v7a/libkissfftr.so | Bin 101784 -> 0 bytes .../main/obj/local/armeabi-v7a/libstdc++.a | 1 - .../local/armeabi-v7a/objs/kissff/kiss_fft.o | Bin 23540 -> 0 bytes .../armeabi-v7a/objs/kissff/kiss_fft.o.d | 8 - .../kissfft/euphony_lib_receiver_KissFFT.o | Bin 65812 -> 0 bytes .../kissfft/euphony_lib_receiver_KissFFT.o.d | 11 - .../armeabi-v7a/objs/kissfftr/kiss_fftr.o | Bin 11340 -> 0 bytes .../armeabi-v7a/objs/kissfftr/kiss_fftr.o.d | 11 - .../main/res/drawable-hdpi/ic_launcher.png | Bin 7658 -> 0 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 3777 -> 0 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 12516 -> 0 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 24777 -> 0 bytes .../src/main/res/values-sw600dp/dimens.xml | 8 - .../main/res/values-sw720dp-land/dimens.xml | 8 - euphony/src/main/res/values/dimens.xml | 5 - euphony/src/main/res/values/strings.xml | 6 - euphony/src/main/res/values/styles.xml | 20 - .../lib/receiver/ReceiverUnitTest.java | 2 +- .../lib/transmitter/TransmitterUnitTest.java | 5 +- .../java/euphony/lib/util/UtilUnitTest.java | 5 +- settings.gradle | 5 +- 89 files changed, 311 insertions(+), 2223 deletions(-) create mode 100644 euphony/src/main/cpp/CMakeLists.txt rename euphony/src/main/{jni => cpp/arms}/_kiss_fft_guts.h (100%) rename euphony/src/main/{jni => cpp/arms}/kiss_fft.c (100%) rename euphony/src/main/{jni => cpp/arms}/kiss_fft.h (100%) rename euphony/src/main/{jni => cpp/arms}/kiss_fftr.c (100%) rename euphony/src/main/{jni => cpp/arms}/kiss_fftr.h (100%) create mode 100644 euphony/src/main/cpp/native-legacy.cpp rename euphony/src/main/java/{euphony/lib/receiver => co/euphony/rx}/AcousticSensor.java (69%) rename euphony/src/main/java/{euphony/lib/receiver => co/euphony/rx}/AudioRecorder.java (97%) rename euphony/src/main/java/{euphony/lib/receiver => co/euphony/rx}/EuDataDecoder.java (91%) rename euphony/src/main/java/{euphony/lib/receiver => co/euphony/rx}/EuFreqObject.java (97%) rename euphony/src/main/java/{euphony/lib/receiver => co/euphony/rx}/EuRxManager.java (98%) rename euphony/src/main/java/{euphony/lib/receiver => co/euphony/rx}/EuWindows.java (99%) rename euphony/src/main/java/{euphony/lib/receiver => co/euphony/rx}/KissFFT.java (68%) rename euphony/src/main/java/{euphony/lib/receiver => co/euphony/rx}/PositionDetector.java (76%) rename euphony/src/main/java/{euphony/lib/transmitter => co/euphony/tx}/EuCodeMaker.java (98%) rename euphony/src/main/java/{euphony/lib/transmitter => co/euphony/tx}/EuDataEncoder.java (89%) rename euphony/src/main/java/{euphony/lib/transmitter => co/euphony/tx}/EuFreqGenerator.java (98%) rename euphony/src/main/java/{euphony/lib/transmitter => co/euphony/tx}/EuPlayer.java (93%) rename euphony/src/main/java/{euphony/lib/transmitter => co/euphony/tx}/EuTxManager.java (97%) rename euphony/src/main/java/{euphony/lib => co/euphony}/util/COMMON.java (94%) rename euphony/src/main/java/{euphony/lib => co/euphony}/util/ErrorHandler.java (99%) rename euphony/src/main/java/{euphony/lib => co/euphony}/util/EuCodec.java (94%) rename euphony/src/main/java/{euphony/lib => co/euphony}/util/EuOption.java (97%) rename euphony/src/main/java/{euphony/lib => co/euphony}/util/PacketErrorDetector.java (99%) delete mode 100644 euphony/src/main/java/euphony/lib/util/EuAfRangeFinder.java delete mode 100644 euphony/src/main/jni/Android.mk delete mode 100644 euphony/src/main/jni/Application.mk delete mode 100644 euphony/src/main/jni/com_euphony_fft_KissFFT.h delete mode 100644 euphony/src/main/jni/euphony_lib_receiver_KissFFT.cpp delete mode 100644 euphony/src/main/jni/euphony_lib_receiver_KissFFT.h delete mode 100644 euphony/src/main/kotlin/euphony/lib/receiver/AcousticSensor.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/receiver/AudioRecorder.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/receiver/EuDataDecoder.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/receiver/EuFreqObject.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/receiver/EuRxManager.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/receiver/EuWindows.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/receiver/KissFFT.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/receiver/PositionDetector.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/transmitter/EuCodeMaker.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/transmitter/EuDataEncoder.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/transmitter/EuFreqGenerator.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/transmitter/EuPlayer.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/transmitter/EuTxManager.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/util/COMMON.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/util/EuCodec.kt delete mode 100644 euphony/src/main/kotlin/euphony/lib/util/PacketErrorDetector.kt delete mode 100755 euphony/src/main/libs/arm64-v8a/libkissff.so delete mode 100755 euphony/src/main/libs/arm64-v8a/libkissfft.so delete mode 100755 euphony/src/main/libs/arm64-v8a/libkissfftr.so delete mode 100755 euphony/src/main/libs/armeabi-v7a/libkissff.so delete mode 100755 euphony/src/main/libs/armeabi-v7a/libkissfft.so delete mode 100755 euphony/src/main/libs/armeabi-v7a/libkissfftr.so delete mode 100755 euphony/src/main/obj/local/arm64-v8a/libkissff.so delete mode 100755 euphony/src/main/obj/local/arm64-v8a/libkissfft.so delete mode 100755 euphony/src/main/obj/local/arm64-v8a/libkissfftr.so delete mode 100644 euphony/src/main/obj/local/arm64-v8a/libstdc++.a delete mode 100644 euphony/src/main/obj/local/arm64-v8a/objs/kissff/kiss_fft.o delete mode 100644 euphony/src/main/obj/local/arm64-v8a/objs/kissff/kiss_fft.o.d delete mode 100644 euphony/src/main/obj/local/arm64-v8a/objs/kissfft/euphony_lib_receiver_KissFFT.o delete mode 100644 euphony/src/main/obj/local/arm64-v8a/objs/kissfft/euphony_lib_receiver_KissFFT.o.d delete mode 100644 euphony/src/main/obj/local/arm64-v8a/objs/kissfftr/kiss_fftr.o delete mode 100644 euphony/src/main/obj/local/arm64-v8a/objs/kissfftr/kiss_fftr.o.d delete mode 100755 euphony/src/main/obj/local/armeabi-v7a/libkissff.so delete mode 100755 euphony/src/main/obj/local/armeabi-v7a/libkissfft.so delete mode 100755 euphony/src/main/obj/local/armeabi-v7a/libkissfftr.so delete mode 100644 euphony/src/main/obj/local/armeabi-v7a/libstdc++.a delete mode 100644 euphony/src/main/obj/local/armeabi-v7a/objs/kissff/kiss_fft.o delete mode 100644 euphony/src/main/obj/local/armeabi-v7a/objs/kissff/kiss_fft.o.d delete mode 100644 euphony/src/main/obj/local/armeabi-v7a/objs/kissfft/euphony_lib_receiver_KissFFT.o delete mode 100644 euphony/src/main/obj/local/armeabi-v7a/objs/kissfft/euphony_lib_receiver_KissFFT.o.d delete mode 100644 euphony/src/main/obj/local/armeabi-v7a/objs/kissfftr/kiss_fftr.o delete mode 100644 euphony/src/main/obj/local/armeabi-v7a/objs/kissfftr/kiss_fftr.o.d delete mode 100644 euphony/src/main/res/drawable-hdpi/ic_launcher.png delete mode 100644 euphony/src/main/res/drawable-mdpi/ic_launcher.png delete mode 100644 euphony/src/main/res/drawable-xhdpi/ic_launcher.png delete mode 100644 euphony/src/main/res/drawable-xxhdpi/ic_launcher.png delete mode 100644 euphony/src/main/res/values-sw600dp/dimens.xml delete mode 100644 euphony/src/main/res/values-sw720dp-land/dimens.xml delete mode 100644 euphony/src/main/res/values/dimens.xml delete mode 100644 euphony/src/main/res/values/strings.xml delete mode 100644 euphony/src/main/res/values/styles.xml diff --git a/build.gradle b/build.gradle index 66dc0de..4ae35ce 100644 --- a/build.gradle +++ b/build.gradle @@ -1,29 +1,33 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.41' repositories { mavenCentral() + google() maven { url "https://plugins.gradle.org/m2/" } - google() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' - classpath 'io.github.gradle-nexus:publish-plugin:1.1.0' - if(gradle.language.equals("kotlin")) classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.android.tools.build:gradle:4.2.2' + + // for mavenCentral publish + //classpath 'io.github.gradle-nexus:publish-plugin:1.1.0' } } +/* + * mavenCentral publish script + * apply plugin: 'io.github.gradle-nexus.publish-plugin' apply from: file("publish-root.gradle") +*/ allprojects { repositories { mavenCentral() + google() maven { url "https://plugins.gradle.org/m2/" } - google() } } \ No newline at end of file diff --git a/euphony/build.gradle b/euphony/build.gradle index c09c19f..7b1c05f 100644 --- a/euphony/build.gradle +++ b/euphony/build.gradle @@ -1,41 +1,21 @@ apply plugin: 'com.android.library' -if(gradle.language.equals("kotlin")) { - apply plugin: 'kotlin-android-extensions' - apply plugin: 'kotlin-android' -} - -apply from: file('publish.gradle') android { compileSdkVersion(30) -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - if(gradle.language.equals("kotlin")) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test:runner:1.3.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' -} -android { defaultConfig { - minSdkVersion 14 + minSdkVersion 16 targetSdkVersion 30 - ndk { - moduleName "kissff" - } - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - sourceSets { - main { - java.srcDirs = ['src/main/' + gradle.language] - } - test { - java.srcDirs = ['src/test/' + gradle.language] + /* This externalNativeBuild block is different from the one which is on the outside of defaultConfig */ + externalNativeBuild { + cmake { + cppFlags "-std=c++14 -fopenmp" + arguments "-DANDROID_STL=c++_shared" + arguments "-DANDROID_SDK_ROOT=${android.getSdkDirectory().getAbsolutePath()}" + } } } @@ -49,13 +29,30 @@ android { testCoverageEnabled false } } + externalNativeBuild { - ndkBuild { - path file('src/main/jni/Android.mk') + cmake { + path "src/main/cpp/CMakeLists.txt" + version "3.10.2" } } + lintOptions { + warning 'MissingPermission' + } + + buildFeatures { + prefab true + } + testOptions { unitTests.returnDefaultValues = true } +} + +dependencies { + implementation 'com.google.oboe:oboe:1.6.1' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' } \ No newline at end of file diff --git a/euphony/src/main/AndroidManifest.xml b/euphony/src/main/AndroidManifest.xml index 834086e..3c4fa8e 100644 --- a/euphony/src/main/AndroidManifest.xml +++ b/euphony/src/main/AndroidManifest.xml @@ -1,9 +1,7 @@ - - diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..dd0972d --- /dev/null +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(euphony + VERSION 0.8) + +#Legacy JNI +set(LEGACY_SRC + arms/kiss_fft.c + arms/kiss_fftr.c + native-legacy.cpp + ) + +find_library( log-lib log ) +add_library( native-legacy SHARED ${LEGACY_SRC} ) +target_compile_definitions(native-legacy PUBLIC FIXED_POINT=16) +target_link_libraries( + native-legacy + ${log-lib} + -fopenmp + -static-openmp +) diff --git a/euphony/src/main/jni/_kiss_fft_guts.h b/euphony/src/main/cpp/arms/_kiss_fft_guts.h similarity index 100% rename from euphony/src/main/jni/_kiss_fft_guts.h rename to euphony/src/main/cpp/arms/_kiss_fft_guts.h diff --git a/euphony/src/main/jni/kiss_fft.c b/euphony/src/main/cpp/arms/kiss_fft.c similarity index 100% rename from euphony/src/main/jni/kiss_fft.c rename to euphony/src/main/cpp/arms/kiss_fft.c diff --git a/euphony/src/main/jni/kiss_fft.h b/euphony/src/main/cpp/arms/kiss_fft.h similarity index 100% rename from euphony/src/main/jni/kiss_fft.h rename to euphony/src/main/cpp/arms/kiss_fft.h diff --git a/euphony/src/main/jni/kiss_fftr.c b/euphony/src/main/cpp/arms/kiss_fftr.c similarity index 100% rename from euphony/src/main/jni/kiss_fftr.c rename to euphony/src/main/cpp/arms/kiss_fftr.c diff --git a/euphony/src/main/jni/kiss_fftr.h b/euphony/src/main/cpp/arms/kiss_fftr.h similarity index 100% rename from euphony/src/main/jni/kiss_fftr.h rename to euphony/src/main/cpp/arms/kiss_fftr.h diff --git a/euphony/src/main/cpp/native-legacy.cpp b/euphony/src/main/cpp/native-legacy.cpp new file mode 100644 index 0000000..22a1934 --- /dev/null +++ b/euphony/src/main/cpp/native-legacy.cpp @@ -0,0 +1,157 @@ +// +// Created by desig on 2020-07-16. +// + +#include +#include +#include "arms/kiss_fftr.h" +#include +#include +#include + +#define MAX_SHORT 32767.0f + +static inline float scale( kiss_fft_scalar val ) +{ + if( val < 0 ) + return val * ( 1 / 32768.0f ); + else + return val * ( 1 / 32767.0f ); +} + +struct KissFFT +{ + kiss_fftr_cfg config; + kiss_fft_cpx* spectrum; + int numSamples; +}; + +extern "C" JNIEXPORT jlong +Java_co_euphony_rx_KissFFT_create(JNIEnv *env, jobject thiz, jint numSamples) { + KissFFT* fft = new KissFFT(); + + fft->config = kiss_fftr_alloc(numSamples,0,nullptr,nullptr); + fft->spectrum = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * (int)numSamples); + fft->numSamples = numSamples; + return (jlong)fft; +} + +/* + * Class: euphony_lib_receiver_KissFFT + * Method: destroy + * Signature: (J)V + */ +extern "C" +JNIEXPORT void JNICALL +Java_co_euphony_rx_KissFFT_destroy(JNIEnv *env, jobject thiz, jlong handle) { + KissFFT* fft = (KissFFT*)handle; + free(fft->config); + free(fft->spectrum); + free(fft); +} + +extern "C" +JNIEXPORT void JNICALL +Java_co_euphony_rx_KissFFT_doSpectrum(JNIEnv *env, jobject thiz, jlong handle, jobject source, + jint sample_idx, jlong mag_address, jlong dbfs_address) { + KissFFT* fft = (KissFFT*)handle; + kiss_fft_scalar* samples = (kiss_fft_scalar*)env->GetDirectBufferAddress( source ); + float* mag = (float*) mag_address; + float* dbfs = (float*) dbfs_address; + + kiss_fftr( fft->config, samples, fft->spectrum ); + + float re = scale(fft->spectrum[sample_idx].r) * fft->numSamples; + float im = scale(fft->spectrum[sample_idx].i) * fft->numSamples; + + *mag = sqrtf( re * re + im * im ) / (fft->numSamples / 2); + *dbfs = 10 * log10(4 * (re * re + im * im) / (fft->numSamples * fft->numSamples)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_co_euphony_rx_KissFFT_doSpectrums(JNIEnv *env, jobject thiz, jlong handle, + jobject source, jobject target) { + KissFFT* fft = (KissFFT*)handle; + kiss_fft_scalar* samples = (kiss_fft_scalar*)env->GetDirectBufferAddress( source ); + float* spectrum = (float*)env->GetDirectBufferAddress( target ); + + + kiss_fftr( fft->config, samples, fft->spectrum ); //<--- fatal signal 11 (SIGSEV) at 0x00000400 + + int len = fft->numSamples / 2 + 1; // <=--- <--- fatal signal 11 (SIGSEV) at 0x00000408 + int len_halfOfNumSamples = fft->numSamples / 2; + // int len = 6; // <-- for debugging + int start = len * (17500.0 / 22050.0); + for( int i = start; i < len; i++ ) + { + float re = scale(fft->spectrum[i].r) * fft->numSamples; + float im = scale(fft->spectrum[i].i) * fft->numSamples; + + // mag(k) = 2 * SQUARE_ROOT(re * re + im * im) / number of samples + spectrum[i] = sqrtf(re*re + im*im) / len_halfOfNumSamples; + + /* + if( i > 0 ) spectrum[i] = sqrtf(re*re + im*im) / (fft->numSamples / 2); + else spectrum[i] = sqrtf(re*re + im*im) / (fft->numSamples); + */ + } +} + + +/* + * Class: euphony_lib_receiver_KissFFT + * Method: spectrum_for_phase + * Signature: (JLjava/nio/ShortBuffer;Ljava/nio/FloatBuffer;)V + */ +extern "C" +JNIEXPORT void JNICALL +Java_co_euphony_rx_KissFFT_spectrum_1for_1phase(JNIEnv *env, jobject thiz, jlong handle, + jobject source, jobject target) { + KissFFT* fft = (KissFFT*)handle; + kiss_fft_scalar* samples = (kiss_fft_scalar*)env->GetDirectBufferAddress( source ); + float* spectrum = (float*)env->GetDirectBufferAddress( target ); + + kiss_fftr( fft->config, samples, fft->spectrum ); + + int len = fft->numSamples / 2 + 1; + int start = (int)(len * (16500.0 / 22050.0)); + + for( int i = start; i < len; i++ ) + { + float re = scale(fft->spectrum[i].r) * fft->numSamples; + float im = scale(fft->spectrum[i].i) * fft->numSamples; + + spectrum[i] = atan2(im,re) * 180.0 / 3.141592; + } +} + +/* + * Class: euphony_lib_receiver_KissFFT + * Method: getRealPart + * Signature: (JLjava/nio/ShortBuffer;)V + */ +extern "C" +JNIEXPORT void JNICALL +Java_co_euphony_rx_KissFFT_getRealPart(JNIEnv *env, jobject thiz, jlong handle, + jobject real) { + KissFFT* fft = (KissFFT*)handle; + short* target = (short*)env->GetDirectBufferAddress(real); + for( int i = 0; i < fft->numSamples / 2; i++ ) + target[i] = fft->spectrum[i].r; +} + +/* + * Class: euphony_lib_receiver_KissFFT + * Method: getImagPart + * Signature: (JLjava/nio/ShortBuffer;)V + */ +extern "C" +JNIEXPORT void JNICALL +Java_co_euphony_rx_KissFFT_getImagePart(JNIEnv *env, jobject thiz, jlong handle, + jobject imag) { + KissFFT* fft = (KissFFT*)handle; + short* target = (short*)env->GetDirectBufferAddress(imag); + for( int i = 0; i < fft->numSamples / 2; i++ ) + target[i] = fft->spectrum[i].i; +} \ No newline at end of file diff --git a/euphony/src/main/java/euphony/lib/receiver/AcousticSensor.java b/euphony/src/main/java/co/euphony/rx/AcousticSensor.java similarity index 69% rename from euphony/src/main/java/euphony/lib/receiver/AcousticSensor.java rename to euphony/src/main/java/co/euphony/rx/AcousticSensor.java index fffd91a..4e31784 100644 --- a/euphony/src/main/java/euphony/lib/receiver/AcousticSensor.java +++ b/euphony/src/main/java/co/euphony/rx/AcousticSensor.java @@ -1,4 +1,4 @@ -package euphony.lib.receiver; +package co.euphony.rx; public interface AcousticSensor { void notify(String letters); diff --git a/euphony/src/main/java/euphony/lib/receiver/AudioRecorder.java b/euphony/src/main/java/co/euphony/rx/AudioRecorder.java similarity index 97% rename from euphony/src/main/java/euphony/lib/receiver/AudioRecorder.java rename to euphony/src/main/java/co/euphony/rx/AudioRecorder.java index c624bcc..6ab5297 100644 --- a/euphony/src/main/java/euphony/lib/receiver/AudioRecorder.java +++ b/euphony/src/main/java/co/euphony/rx/AudioRecorder.java @@ -1,11 +1,10 @@ -package euphony.lib.receiver; +package co.euphony.rx; import java.nio.ByteBuffer; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; -import android.util.Log; public class AudioRecorder { diff --git a/euphony/src/main/java/euphony/lib/receiver/EuDataDecoder.java b/euphony/src/main/java/co/euphony/rx/EuDataDecoder.java similarity index 91% rename from euphony/src/main/java/euphony/lib/receiver/EuDataDecoder.java rename to euphony/src/main/java/co/euphony/rx/EuDataDecoder.java index 279fbbd..60fbef7 100644 --- a/euphony/src/main/java/euphony/lib/receiver/EuDataDecoder.java +++ b/euphony/src/main/java/co/euphony/rx/EuDataDecoder.java @@ -1,7 +1,6 @@ -package euphony.lib.receiver; +package co.euphony.rx; -import android.util.Log; -import euphony.lib.util.EuCodec; +import co.euphony.util.EuCodec; public class EuDataDecoder extends EuCodec { private String mOriginalSource; diff --git a/euphony/src/main/java/euphony/lib/receiver/EuFreqObject.java b/euphony/src/main/java/co/euphony/rx/EuFreqObject.java similarity index 97% rename from euphony/src/main/java/euphony/lib/receiver/EuFreqObject.java rename to euphony/src/main/java/co/euphony/rx/EuFreqObject.java index 6cfd635..ecd340c 100644 --- a/euphony/src/main/java/euphony/lib/receiver/EuFreqObject.java +++ b/euphony/src/main/java/co/euphony/rx/EuFreqObject.java @@ -1,4 +1,4 @@ -package euphony.lib.receiver; +package co.euphony.rx; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -7,8 +7,8 @@ import java.util.ArrayList; import android.util.Log; -import euphony.lib.util.COMMON; -import euphony.lib.util.PacketErrorDetector; +import co.euphony.util.COMMON; +import co.euphony.util.PacketErrorDetector; public class EuFreqObject { @@ -98,10 +98,7 @@ public void processFFT() isRecording = true; } recorder.read(samples, fftsize); - FFT.spectrum(samples, spectrum); - //FFT.spectrum_for_phase(samples, spectrum_p); - //FFT.getRealPart(real); - //FFT.getImagPart(image); + FFT.doSpectrums(samples, spectrum); } public void processFFT(short windowsNum) @@ -111,7 +108,7 @@ public void processFFT(short windowsNum) isRecording = true; } recorder.read(samples, fftsize, windowsNum); - FFT.spectrum(samples, spectrum); + FFT.doSpectrums(samples, spectrum); } public void destroyFFT() diff --git a/euphony/src/main/java/euphony/lib/receiver/EuRxManager.java b/euphony/src/main/java/co/euphony/rx/EuRxManager.java similarity index 98% rename from euphony/src/main/java/euphony/lib/receiver/EuRxManager.java rename to euphony/src/main/java/co/euphony/rx/EuRxManager.java index 0f0ad86..54f1321 100644 --- a/euphony/src/main/java/euphony/lib/receiver/EuRxManager.java +++ b/euphony/src/main/java/co/euphony/rx/EuRxManager.java @@ -1,6 +1,6 @@ -package euphony.lib.receiver; +package co.euphony.rx; -import euphony.lib.util.COMMON; +import co.euphony.util.COMMON; import android.os.Handler; import android.os.Message; import android.util.Log; diff --git a/euphony/src/main/java/euphony/lib/receiver/EuWindows.java b/euphony/src/main/java/co/euphony/rx/EuWindows.java similarity index 99% rename from euphony/src/main/java/euphony/lib/receiver/EuWindows.java rename to euphony/src/main/java/co/euphony/rx/EuWindows.java index f9c56c3..8f50648 100644 --- a/euphony/src/main/java/euphony/lib/receiver/EuWindows.java +++ b/euphony/src/main/java/co/euphony/rx/EuWindows.java @@ -1,4 +1,4 @@ -package euphony.lib.receiver; +package co.euphony.rx; import java.nio.ByteBuffer; import java.util.*; diff --git a/euphony/src/main/java/euphony/lib/receiver/KissFFT.java b/euphony/src/main/java/co/euphony/rx/KissFFT.java similarity index 68% rename from euphony/src/main/java/euphony/lib/receiver/KissFFT.java rename to euphony/src/main/java/co/euphony/rx/KissFFT.java index e7ae292..b9bd225 100644 --- a/euphony/src/main/java/euphony/lib/receiver/KissFFT.java +++ b/euphony/src/main/java/co/euphony/rx/KissFFT.java @@ -1,4 +1,4 @@ -package euphony.lib.receiver; +package co.euphony.rx; import java.nio.ByteBuffer; @@ -9,90 +9,59 @@ public class KissFFT { - static{ - System.loadLibrary("kissff"); // kiss_fft.c - System.loadLibrary("kissfftr"); // kiss_fftr.c - System.loadLibrary("kissfft"); // KissFFT.cpp - } + static{ + System.loadLibrary("native-legacy"); + } /** the pointer to the kiss fft object **/ private final long handle; - + public KissFFT(int numSamples) { handle = create(numSamples); Log.i("var", " "+handle); } - /** - * Creates a new kiss fft object - * - * @param timeSize - * the number of samples - * @return the handle to the kiss fft object - */ - private native long create(int timeSize); - - /** - * Destroys a kiss fft object - * - * @param handle - * the handle to the kiss fft object - */ - private native void destroy(long handle); - - /** - * Calculates the frequency spectrum of the given samples. There must be as - * many samples as specified in the constructor of this class. Spectrum must - * hold timeSize / 2 + 1 elements - * - * @param handle - * the handle to the kiss fft object - * @param samples - * the samples in 16-bit signed PCM encoding - * @param spectrum - * the spectrum - */ - private native void spectrum(long handle, ShortBuffer samples, - FloatBuffer spectrum); - /** * Calculates the frequency spectrum of the given samples. There must be as * many samples as specified in the constructor of this class. Spectrum must * hold timeSize / 2 + 1 elements - * + * * @param samples * the samples * @param spectrum * the spectrum */ - public void spectrum(ShortBuffer samples, FloatBuffer spectrum) { - spectrum(handle, samples, spectrum); + public void doSpectrums(ShortBuffer samples, FloatBuffer spectrum) { + doSpectrums(handle, samples, spectrum); } /** * Calculates the frequency spectrum of the given samples. There must be as * many samples as specified in the constructor of this class. Spectrum must * hold timeSize / 2 + 1 elements - * + * * @param samples * the samples * @param spectrum * the spectrum */ - public void spectrum(ByteBuffer samples, FloatBuffer spectrum) { - spectrum(samples.asShortBuffer(), spectrum); + public void doSpectrums(ByteBuffer samples, FloatBuffer spectrum) { + doSpectrums(samples.asShortBuffer(), spectrum); + } + + public void doSpectrum(ShortBuffer samples, int sample_idx, long mag_result_address, long dbfs_result_addr) + { + doSpectrum(handle, samples, sample_idx, mag_result_address, dbfs_result_addr); } - private native void spectrum_for_phase(long handle, ShortBuffer samples, FloatBuffer specturm); - public void spectrum_for_phase(ShortBuffer samples, FloatBuffer spectrum) { - spectrum_for_phase(handle, samples, spectrum); + spectrum_for_phase(handle, samples, spectrum); } - + public void spectrum_for_phase(ByteBuffer samples, FloatBuffer spectrum) { - spectrum_for_phase(samples.asShortBuffer(), spectrum); + spectrum_for_phase(samples.asShortBuffer(), spectrum); } /** * Releases all resources of this object @@ -105,19 +74,48 @@ public void getRealPart(ShortBuffer real) { getRealPart(handle, real); } - public void getImagPart(ShortBuffer imag) { - getImagPart(handle, imag); + public void getImagePart(ShortBuffer imag) { + getImagePart(handle, imag); } + /** + * Creates a new kiss fft object + * + * @param timeSize + * the number of samples + * @return the handle to the kiss fft object + */ + private native long create(int timeSize); + + /** + * Destroys a kiss fft object + * + * @param handle + * the handle to the kiss fft object + */ + private native void destroy(long handle); + + private native void doSpectrum(long handle, ShortBuffer samples, int sample_idx, long mag_result, long dbfs_result); + + /** + * Calculates the frequency spectrum of the given samples. There must be as + * many samples as specified in the constructor of this class. Spectrum must + * hold timeSize / 2 + 1 elements + * + * @param handle + * the handle to the kiss fft object + * @param samples + * the samples in 16-bit signed PCM encoding + * @param spectrum + * the spectrum + */ + private native void doSpectrums(long handle, ShortBuffer samples, + FloatBuffer spectrum); + + private native void spectrum_for_phase(long handle, ShortBuffer samples, FloatBuffer specturm); + private native void getRealPart(long handle, ShortBuffer real); - private native void getImagPart(long handle, ShortBuffer imag); - - - - - - - + private native void getImagePart(long handle, ShortBuffer imag); } \ No newline at end of file diff --git a/euphony/src/main/java/euphony/lib/receiver/PositionDetector.java b/euphony/src/main/java/co/euphony/rx/PositionDetector.java similarity index 76% rename from euphony/src/main/java/euphony/lib/receiver/PositionDetector.java rename to euphony/src/main/java/co/euphony/rx/PositionDetector.java index 2b3eb04..086f1c2 100644 --- a/euphony/src/main/java/euphony/lib/receiver/PositionDetector.java +++ b/euphony/src/main/java/co/euphony/rx/PositionDetector.java @@ -1,4 +1,4 @@ -package euphony.lib.receiver; +package co.euphony.rx; public interface PositionDetector { void detectSignal(int signal); diff --git a/euphony/src/main/java/euphony/lib/transmitter/EuCodeMaker.java b/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java similarity index 98% rename from euphony/src/main/java/euphony/lib/transmitter/EuCodeMaker.java rename to euphony/src/main/java/co/euphony/tx/EuCodeMaker.java index 4c553af..85c3f4f 100644 --- a/euphony/src/main/java/euphony/lib/transmitter/EuCodeMaker.java +++ b/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java @@ -1,7 +1,7 @@ -package euphony.lib.transmitter; +package co.euphony.tx; import android.util.Log; -import euphony.lib.util.PacketErrorDetector; +import co.euphony.util.PacketErrorDetector; public class EuCodeMaker extends EuFreqGenerator { String mMainString; diff --git a/euphony/src/main/java/euphony/lib/transmitter/EuDataEncoder.java b/euphony/src/main/java/co/euphony/tx/EuDataEncoder.java similarity index 89% rename from euphony/src/main/java/euphony/lib/transmitter/EuDataEncoder.java rename to euphony/src/main/java/co/euphony/tx/EuDataEncoder.java index 1c4a7df..e89a702 100644 --- a/euphony/src/main/java/euphony/lib/transmitter/EuDataEncoder.java +++ b/euphony/src/main/java/co/euphony/tx/EuDataEncoder.java @@ -1,11 +1,6 @@ -package euphony.lib.transmitter; +package co.euphony.tx; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Arrays; - -import euphony.lib.util.EuCodec; +import co.euphony.util.EuCodec; public class EuDataEncoder extends EuCodec { private String mOriginalSource; diff --git a/euphony/src/main/java/euphony/lib/transmitter/EuFreqGenerator.java b/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java similarity index 98% rename from euphony/src/main/java/euphony/lib/transmitter/EuFreqGenerator.java rename to euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java index a051eb5..69577ab 100644 --- a/euphony/src/main/java/euphony/lib/transmitter/EuFreqGenerator.java +++ b/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java @@ -1,6 +1,6 @@ -package euphony.lib.transmitter; +package co.euphony.tx; -import euphony.lib.util.COMMON; +import co.euphony.util.COMMON; public class EuFreqGenerator { diff --git a/euphony/src/main/java/euphony/lib/transmitter/EuPlayer.java b/euphony/src/main/java/co/euphony/tx/EuPlayer.java similarity index 93% rename from euphony/src/main/java/euphony/lib/transmitter/EuPlayer.java rename to euphony/src/main/java/co/euphony/tx/EuPlayer.java index da741e8..ca7624b 100644 --- a/euphony/src/main/java/euphony/lib/transmitter/EuPlayer.java +++ b/euphony/src/main/java/co/euphony/tx/EuPlayer.java @@ -1,6 +1,6 @@ -package euphony.lib.transmitter; +package co.euphony.tx; -import euphony.lib.util.COMMON; +import co.euphony.util.COMMON; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; diff --git a/euphony/src/main/java/euphony/lib/transmitter/EuTxManager.java b/euphony/src/main/java/co/euphony/tx/EuTxManager.java similarity index 97% rename from euphony/src/main/java/euphony/lib/transmitter/EuTxManager.java rename to euphony/src/main/java/co/euphony/tx/EuTxManager.java index dc5529c..6f9cc4e 100644 --- a/euphony/src/main/java/euphony/lib/transmitter/EuTxManager.java +++ b/euphony/src/main/java/co/euphony/tx/EuTxManager.java @@ -1,4 +1,4 @@ -package euphony.lib.transmitter; +package co.euphony.tx; import android.content.Context; import android.media.AudioFormat; @@ -6,7 +6,7 @@ import android.media.AudioTrack; import android.util.Log; -import euphony.lib.util.EuOption; +import co.euphony.util.EuOption; public class EuTxManager { private AudioTrack mAudioTrack = null; diff --git a/euphony/src/main/java/euphony/lib/util/COMMON.java b/euphony/src/main/java/co/euphony/util/COMMON.java similarity index 94% rename from euphony/src/main/java/euphony/lib/util/COMMON.java rename to euphony/src/main/java/co/euphony/util/COMMON.java index 2da1657..920309d 100644 --- a/euphony/src/main/java/euphony/lib/util/COMMON.java +++ b/euphony/src/main/java/co/euphony/util/COMMON.java @@ -1,4 +1,4 @@ -package euphony.lib.util; +package co.euphony.util; public interface COMMON { // RX & TX COMMON VARIABLES diff --git a/euphony/src/main/java/euphony/lib/util/ErrorHandler.java b/euphony/src/main/java/co/euphony/util/ErrorHandler.java similarity index 99% rename from euphony/src/main/java/euphony/lib/util/ErrorHandler.java rename to euphony/src/main/java/co/euphony/util/ErrorHandler.java index 0bcacbd..2b0b793 100644 --- a/euphony/src/main/java/euphony/lib/util/ErrorHandler.java +++ b/euphony/src/main/java/co/euphony/util/ErrorHandler.java @@ -1,4 +1,4 @@ -package euphony.lib.util; +package co.euphony.util; import android.util.Log; diff --git a/euphony/src/main/java/euphony/lib/util/EuCodec.java b/euphony/src/main/java/co/euphony/util/EuCodec.java similarity index 94% rename from euphony/src/main/java/euphony/lib/util/EuCodec.java rename to euphony/src/main/java/co/euphony/util/EuCodec.java index cfaf4ba..f7351ef 100644 --- a/euphony/src/main/java/euphony/lib/util/EuCodec.java +++ b/euphony/src/main/java/co/euphony/util/EuCodec.java @@ -1,4 +1,4 @@ -package euphony.lib.util; +package co.euphony.util; import java.util.Arrays; diff --git a/euphony/src/main/java/euphony/lib/util/EuOption.java b/euphony/src/main/java/co/euphony/util/EuOption.java similarity index 97% rename from euphony/src/main/java/euphony/lib/util/EuOption.java rename to euphony/src/main/java/co/euphony/util/EuOption.java index 8f49755..887ef7d 100644 --- a/euphony/src/main/java/euphony/lib/util/EuOption.java +++ b/euphony/src/main/java/co/euphony/util/EuOption.java @@ -1,4 +1,4 @@ -package euphony.lib.util; +package co.euphony.util; public class EuOption { public enum EncodingType { diff --git a/euphony/src/main/java/euphony/lib/util/PacketErrorDetector.java b/euphony/src/main/java/co/euphony/util/PacketErrorDetector.java similarity index 99% rename from euphony/src/main/java/euphony/lib/util/PacketErrorDetector.java rename to euphony/src/main/java/co/euphony/util/PacketErrorDetector.java index 8242a5b..41bd564 100644 --- a/euphony/src/main/java/euphony/lib/util/PacketErrorDetector.java +++ b/euphony/src/main/java/co/euphony/util/PacketErrorDetector.java @@ -1,4 +1,4 @@ -package euphony.lib.util; +package co.euphony.util; import android.util.Log; diff --git a/euphony/src/main/java/euphony/lib/util/EuAfRangeFinder.java b/euphony/src/main/java/euphony/lib/util/EuAfRangeFinder.java deleted file mode 100644 index 3554633..0000000 --- a/euphony/src/main/java/euphony/lib/util/EuAfRangeFinder.java +++ /dev/null @@ -1,127 +0,0 @@ -package euphony.lib.util; - -import android.os.Handler; -import android.os.Message; -import android.util.Log; - -import euphony.lib.receiver.EuFreqObject; -import euphony.lib.receiver.PositionDetector; -import euphony.lib.transmitter.EuFreqGenerator; -import euphony.lib.transmitter.EuPlayer; - -public class EuAfRangeFinder { - - EuFreqGenerator mEuFreqGenerator; - EuPlayer mEuPlayer; - EuFreqObject mEuObject; - GettingFQRunner mFQRunner; - - private static final int RX_FREQ = 2; - private static final int RX_REF = 3; - - public EuAfRangeFinder() { - ClassInitializer(); - } - - private void ClassInitializer(){ - mEuFreqGenerator = new EuFreqGenerator(); - mEuPlayer = new EuPlayer(); - mEuObject = new EuFreqObject(); - } - - - PositionDetector mPositionDetector; - public void setPositionDetector(PositionDetector detector) { - this.mPositionDetector = detector; - } - - Handler mHandler = new Handler() { - - @Override - public void handleMessage(Message msg) { - // TODO Auto-generated method stub - super.handleMessage(msg); - switch(msg.what) - { - case RX_FREQ: - mSuitableFreq = (Integer)msg.obj; - mPositionDetector.detectFq(mSuitableFreq); - - mFQRunner = null; - break; - } - } - - }; - - int mSuitableFreq; - - public void findSuitableChannel() - { - ClassInitializer(); - mFQRunner = new GettingFQRunner(); - mFQRunner.start(); - } - public void stopFindingChannel() - { - while(mFQRunner.isAlive()) - { - try { - mFQRunner.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - mFQRunner = null; - } - - public - - class GettingFQRunner extends Thread { - - int ref = 0; - int current_freq = 21000;//(int)COMMON.MAX_FREQ - 1000; - - @Override - public void run() { - - do - { - mEuPlayer.setSource(mEuFreqGenerator.applyCrossFade(mEuFreqGenerator.makeStaticFrequency(current_freq, 0))); - try{ mEuPlayer.Play(); } - catch(IllegalStateException e) - { - mEuPlayer = new EuPlayer(); - Log.e("PLAY", e.getMessage().toString()); - continue; - } - int cnt = 0; - do { - mEuObject.processFFT(); - ref = mEuObject.detectFreq(current_freq); - }while(ref <= 250 && cnt++ < 50); - - Log.i("RANGE", ref + " : " + current_freq); - current_freq -= COMMON.CHANNEL_SPAN; - try { mEuPlayer.Stop(); } - catch(IllegalStateException e) - { - mEuPlayer = new EuPlayer(); - Log.e("PLAY", e.getMessage().toString()); - continue; - } - }while(ref <= 250 && current_freq > 16500); - - current_freq += COMMON.CHANNEL_SPAN; - - Message msg = mHandler.obtainMessage(); - msg.what = RX_FREQ; - msg.obj = current_freq; - mHandler.sendMessage(msg); - - mEuObject.destroyFFT(); - } - }; - -} diff --git a/euphony/src/main/jni/Android.mk b/euphony/src/main/jni/Android.mk deleted file mode 100644 index 839d5ad..0000000 --- a/euphony/src/main/jni/Android.mk +++ /dev/null @@ -1,74 +0,0 @@ -LOCAL_PATH := $(call my-dir) -LOCAL_C_INCLUDES += $(LOCAL_PATH)/jni -LOCAL_ALLOW_UNDEFINED_SYMBOLS := true - - -include $(CLEAR_VARS) - - -LOCAL_MODULE := kissff -LOCAL_SRC_FILES := kiss_fft.c -LOCAL_LDLIBS := -llog -LOCAL_ARM_MODE := arm -LOCAL_CFLAGS := -O2 -Wall -D__ANDROID__ -LOCAL_CFLAGS += -DFIXED_POINT - - -include $(BUILD_SHARED_LIBRARY) - - - - - -include $(CLEAR_VARS) - - - -LOCAL_MODULE := kissfftr -LOCAL_SRC_FILES := kiss_fftr.c -LOCAL_LDLIBS := -llog -LOCAL_STATIC_LIBRARIES := kissff -LOCAL_ARM_MODE := arm -LOCAL_CFLAGS := -O2 -Wall -D__ANDROID__ - -LOCAL_CFLAGS += -DFIXED_POINT -include $(BUILD_SHARED_LIBRARY) - - - - - -include $(CLEAR_VARS) - -#APP_STL := gnustl_static -#APP_CPPFLAGS += -frtti -#APP_CPPFLAGS += -fexceptions -#LOCAL_ALLOW_UNDEFINED_SYMBOLS := true -LOCAL_ARM_MODE := arm - -LOCAL_CFLAGS := -O2 -Wall -D__ANDROID__ -LOCAL_CFLAGS += -DFIXED_POINT - - - -LOCAL_MODULE := kissfft -#LOCAL_SRC_FILES := $(wildcard jni/*.c) -LOCAL_SRC_FILES := euphony_lib_receiver_KissFFT.cpp -#LOCAL_SRC_FILES := ca_uol_aig_fftpack_KissFFT.cpp \ kiss_fftr.c \ kiss_fft.c -LOCAL_LDLIBS := -llog - -LOCAL_STATIC_LIBRARIES := kissfftr - - - - - -include $(BUILD_SHARED_LIBRARY) - - - - - - - - diff --git a/euphony/src/main/jni/Application.mk b/euphony/src/main/jni/Application.mk deleted file mode 100644 index 3862d6a..0000000 --- a/euphony/src/main/jni/Application.mk +++ /dev/null @@ -1 +0,0 @@ -APP_ABI := armeabi-v7a, arm64-v8a \ No newline at end of file diff --git a/euphony/src/main/jni/com_euphony_fft_KissFFT.h b/euphony/src/main/jni/com_euphony_fft_KissFFT.h deleted file mode 100644 index 4816245..0000000 --- a/euphony/src/main/jni/com_euphony_fft_KissFFT.h +++ /dev/null @@ -1,53 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class com_euphony_fft_KissFFT */ - -#ifndef _Included_com_euphony_fft_KissFFT -#define _Included_com_euphony_fft_KissFFT -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: com_euphony_fft_KissFFT - * Method: create - * Signature: (I)J - */ -JNIEXPORT jlong JNICALL Java_com_euphony_fft_KissFFT_create - (JNIEnv *, jobject, jint); - -/* - * Class: com_euphony_fft_KissFFT - * Method: destroy - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_euphony_fft_KissFFT_destroy - (JNIEnv *, jobject, jlong); - -/* - * Class: com_euphony_fft_KissFFT - * Method: spectrum - * Signature: (JLjava/nio/ShortBuffer;Ljava/nio/FloatBuffer;)V - */ -JNIEXPORT void JNICALL Java_com_euphony_fft_KissFFT_spectrum - (JNIEnv *, jobject, jlong, jobject, jobject); - -/* - * Class: com_euphony_fft_KissFFT - * Method: getRealPart - * Signature: (JLjava/nio/ShortBuffer;)V - */ -JNIEXPORT void JNICALL Java_com_euphony_fft_KissFFT_getRealPart - (JNIEnv *, jobject, jlong, jobject); - -/* - * Class: com_euphony_fft_KissFFT - * Method: getImagPart - * Signature: (JLjava/nio/ShortBuffer;)V - */ -JNIEXPORT void JNICALL Java_com_euphony_fft_KissFFT_getImagPart - (JNIEnv *, jobject, jlong, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/euphony/src/main/jni/euphony_lib_receiver_KissFFT.cpp b/euphony/src/main/jni/euphony_lib_receiver_KissFFT.cpp deleted file mode 100644 index 6b9d2f3..0000000 --- a/euphony/src/main/jni/euphony_lib_receiver_KissFFT.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include "kiss_fftr.h" -#include "euphony_lib_receiver_KissFFT.h" -#include -#include -#include - -#define MAX_SHORT 32767.0f - -static inline float scale( kiss_fft_scalar val ) -{ - if( val < 0 ) - return val * ( 1 / 32768.0f ); - else - return val * ( 1 / 32767.0f ); -} - -struct KissFFT -{ - kiss_fftr_cfg config; - kiss_fft_cpx* spectrum; - int numSamples; -}; - - -JNIEXPORT jlong JNICALL Java_euphony_lib_receiver_KissFFT_create (JNIEnv *, jobject, jint numSamples) -{ - KissFFT* fft = new KissFFT(); - - fft->config = kiss_fftr_alloc(numSamples,0,NULL,NULL); - fft->spectrum = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * (int)numSamples); - - //__android_log_print(ANDROID_LOG_INFO,"----","r: %f i : %f",fft->spectrum->r, fft->spectrum->i); - fft->numSamples = numSamples; - return (jlong)fft; - return 0; -} - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: destroy - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_destroy(JNIEnv *, jobject, jlong handle) -{ - KissFFT* fft = (KissFFT*)handle; - free(fft->config); - free(fft->spectrum); - free(fft); -} - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: spectrum_for_phase - * Signature: (JLjava/nio/ShortBuffer;Ljava/nio/FloatBuffer;)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_spectrum_1for_1phase - (JNIEnv *env, jobject, jlong handle, jobject source, jobject target) -{ - KissFFT* fft = (KissFFT*)handle; - kiss_fft_scalar* samples = (kiss_fft_scalar*)env->GetDirectBufferAddress( source ); - float* spectrum = (float*)env->GetDirectBufferAddress( target ); - - kiss_fftr( fft->config, samples, fft->spectrum ); - - int len = fft->numSamples / 2 + 1; - int start = len * (16500.0 / 22050.0); - - for( int i = start; i < len; i++ ) - { - float re = scale(fft->spectrum[i].r) * fft->numSamples; - float im = scale(fft->spectrum[i].i) * fft->numSamples; - - spectrum[i] = atan2(im,re) * 180 / 3.141592; - } -} - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: spectrum - * Signature: (JLjava/nio/ShortBuffer;Ljava/nio/FloatBuffer;)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_spectrum(JNIEnv *env, jobject, jlong handle, jobject source, jobject target) -{ - - KissFFT* fft = (KissFFT*)handle; - kiss_fft_scalar* samples = (kiss_fft_scalar*)env->GetDirectBufferAddress( source ); - float* spectrum = (float*)env->GetDirectBufferAddress( target ); - - - kiss_fftr( fft->config, samples, fft->spectrum ); //<--- fatal signal 11 (SIGSEV) at 0x00000400 - - int len = fft->numSamples / 2 + 1; // <=--- <--- fatal signal 11 (SIGSEV) at 0x00000408 - // int len = 6; // <-- for debugging - int start = len * (16500.0 / 22050.0); - for( int i = start; i < len; i++ ) - { - float re = scale(fft->spectrum[i].r) * fft->numSamples; - float im = scale(fft->spectrum[i].i) * fft->numSamples; - - if( i > 0 ) - spectrum[i] = sqrtf(re*re + im*im) / (fft->numSamples / 2); - else - spectrum[i] = sqrtf(re*re + im*im) / (fft->numSamples); - } -} - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: getRealPart - * Signature: (JLjava/nio/ShortBuffer;)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_getRealPart(JNIEnv *env, jobject, jlong handle, jobject real) -{ - KissFFT* fft = (KissFFT*)handle; - short* target = (short*)env->GetDirectBufferAddress(real); - for( int i = 0; i < fft->numSamples / 2; i++ ) - target[i] = fft->spectrum[i].r; -} - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: getImagPart - * Signature: (JLjava/nio/ShortBuffer;)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_getImagPart(JNIEnv *env, jobject, jlong handle, jobject real) -{ - KissFFT* fft = (KissFFT*)handle; - short* target = (short*)env->GetDirectBufferAddress(real); - for( int i = 0; i < fft->numSamples / 2; i++ ) - target[i] = fft->spectrum[i].i; -} diff --git a/euphony/src/main/jni/euphony_lib_receiver_KissFFT.h b/euphony/src/main/jni/euphony_lib_receiver_KissFFT.h deleted file mode 100644 index 694c2ad..0000000 --- a/euphony/src/main/jni/euphony_lib_receiver_KissFFT.h +++ /dev/null @@ -1,61 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class euphony_lib_receiver_KissFFT */ - -#ifndef _Included_euphony_lib_receiver_KissFFT -#define _Included_euphony_lib_receiver_KissFFT -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: euphony_lib_receiver_KissFFT - * Method: create - * Signature: (I)J - */ -JNIEXPORT jlong JNICALL Java_euphony_lib_receiver_KissFFT_create - (JNIEnv *, jobject, jint); - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: destroy - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_destroy - (JNIEnv *, jobject, jlong); - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: spectrum - * Signature: (JLjava/nio/ShortBuffer;Ljava/nio/FloatBuffer;)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_spectrum - (JNIEnv *, jobject, jlong, jobject, jobject); - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: spectrum_for_phase - * Signature: (JLjava/nio/ShortBuffer;Ljava/nio/FloatBuffer;)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_spectrum_1for_1phase - (JNIEnv *, jobject, jlong, jobject, jobject); - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: getRealPart - * Signature: (JLjava/nio/ShortBuffer;)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_getRealPart - (JNIEnv *, jobject, jlong, jobject); - -/* - * Class: euphony_lib_receiver_KissFFT - * Method: getImagPart - * Signature: (JLjava/nio/ShortBuffer;)V - */ -JNIEXPORT void JNICALL Java_euphony_lib_receiver_KissFFT_getImagPart - (JNIEnv *, jobject, jlong, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/euphony/src/main/kotlin/euphony/lib/receiver/AcousticSensor.kt b/euphony/src/main/kotlin/euphony/lib/receiver/AcousticSensor.kt deleted file mode 100644 index 066d650..0000000 --- a/euphony/src/main/kotlin/euphony/lib/receiver/AcousticSensor.kt +++ /dev/null @@ -1,5 +0,0 @@ -package euphony.lib.receiver - -interface AcousticSensor { - fun notify(letters: String) -} diff --git a/euphony/src/main/kotlin/euphony/lib/receiver/AudioRecorder.kt b/euphony/src/main/kotlin/euphony/lib/receiver/AudioRecorder.kt deleted file mode 100644 index 5f4bb3d..0000000 --- a/euphony/src/main/kotlin/euphony/lib/receiver/AudioRecorder.kt +++ /dev/null @@ -1,87 +0,0 @@ -package euphony.lib.receiver - -import android.media.AudioFormat -import android.media.AudioRecord -import android.media.MediaRecorder -import java.nio.ByteBuffer - -class AudioRecorder -/** - * Constructor. - * - * @param sampleRate - * The sample rate (e.g. 44100 Hz). - * @param bufferSize - * The size of the recording buffer in bytes. - */ -(sampleRate: Int) { - - private val audioRecord: AudioRecord - private var running: Boolean = false - private var swtWindowing: Boolean = false //TEST WINDOWING - - init { - var bufferSize = AudioRecord.getMinBufferSize(sampleRate, - AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) - - audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC, - sampleRate, AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT, bufferSize) - swtWindowing = false - } - - /** - * Reads the mic input and writes to a buffer. - * - * @param buffer - * The buffer that will store the samples. - * @param bufferSize - * The max number of bytes to be read. - * @return The number of bytes that were read. - */ - fun read(buffer: ByteBuffer, bufferSize: Int): Int { - val samplesRead: Int - samplesRead = audioRecord.read(buffer, bufferSize) - return samplesRead - } - - fun read(buffer: ByteBuffer, bufferSize: Int, windowNum: Short): Int { - val samplesRead: Int - samplesRead = audioRecord.read(buffer, bufferSize) - swtWindowing = swtWindowing xor true - if (swtWindowing) { - val euWindow = EuWindows(windowNum, buffer, bufferSize) - euWindow.Processor() - } - return samplesRead - } - - /** - * Starts recording audio. Must be called before the first call to 'read'. - */ - fun start() { - if (!running) { - audioRecord.startRecording() - running = true - } - } - - /** - * Stops recording audio. - */ - fun stop() { - if (running) { - audioRecord.stop() - running = false - } - } - - /** - * Releases the allocated memory. - */ - fun destroy() { - stop() - //audioRecord.release(); - } - -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/receiver/EuDataDecoder.kt b/euphony/src/main/kotlin/euphony/lib/receiver/EuDataDecoder.kt deleted file mode 100644 index 78462ef..0000000 --- a/euphony/src/main/kotlin/euphony/lib/receiver/EuDataDecoder.kt +++ /dev/null @@ -1,42 +0,0 @@ -package euphony.lib.receiver - -import android.util.Log -import euphony.lib.util.EuCodec - -class EuDataDecoder(var originalSource: String?) : EuCodec() { - - fun decodeHexCharSource(): String? { - return decodeStaticHexCharSource(originalSource!!) - } - - companion object { - - fun decodeStaticHexCharSource(_source: String?): String? { - var retValue = "" - var i = 0 - - if(_source == null) - return null - - while (i < _source.length - 1) { - val data = Integer.parseInt("" + _source[i] + _source[i + 1], 16) - retValue += data.toChar() - Log.d("DECODED", _source + " : " + _source[i] + " " + _source[i + 1] + " " + retValue) - i += 2 - } - - return retValue - } - - fun decodeStaticHexCharSource(_intArray: IntArray): String { - var retValue = "" - var i = 0 - while (i < _intArray.size - 1) { - retValue += (_intArray[i] * 16 + _intArray[i + 1]).toChar() - i += 2 - } - - return retValue - } - } -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/receiver/EuFreqObject.kt b/euphony/src/main/kotlin/euphony/lib/receiver/EuFreqObject.kt deleted file mode 100644 index f684a1f..0000000 --- a/euphony/src/main/kotlin/euphony/lib/receiver/EuFreqObject.kt +++ /dev/null @@ -1,393 +0,0 @@ -package euphony.lib.receiver - -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.nio.FloatBuffer -import java.nio.ShortBuffer -import java.util.ArrayList - -import android.util.Log -import euphony.lib.util.COMMON -import euphony.lib.util.PacketErrorDetector - -open class EuFreqObject { - - val SAMPLERATE = COMMON.SAMPLERATE//44100; - val fftsize = COMMON.FFT_SIZE//512; - val MAXREFERENCE = COMMON.MAX_REF//4000; - val MINREFERENCE = COMMON.MIN_REF//50; - val MAXFREQUENCY = COMMON.MAX_FREQ//22050.0; - val DEFAULT_REF = COMMON.DEFAULT_REF//500 - val START_FREQ = COMMON.START_FREQ //18000 - val RXCHANNEL = COMMON.CHANNEL// 16 - private val STARTCHANNEL = 1 - private val mFreqSpan = COMMON.CHANNEL_SPAN // 86 - var START_BIT = START_FREQ - mFreqSpan - - var mStartSwt: Boolean? = false - var isCompleted: Boolean? = false - private var isRecording: Boolean? = false - - private val samples = allocateByteBuffer(fftsize) - private val spectrum = allocateFloatBuffer(fftsize / 2 + 1) - private val spectrum_p = allocateFloatBuffer(fftsize / 2 + 1) - var receivedData: String? = null - - private val mFreqArray = IntArray(RXCHANNEL + STARTCHANNEL) - private val mTempRef = IntArray(RXCHANNEL + STARTCHANNEL) - private val mRefCntIndexArray = IntArray(RXCHANNEL + STARTCHANNEL) - private val mDynamicRefArray = IntArray(RXCHANNEL + STARTCHANNEL) - - private val mChannelArrayList = ArrayList() - - private val recorder: AudioRecorder - private val FFT = KissFFT(fftsize) - - private var mMaxIndex = -1 - private var mSampleIndex = 0 - private var mSampleTemp = 0 - private var mChannelTemp = 0 - var cntCheck = 0 - private var mArrMaxIndex = IntArray(RXCHANNEL + STARTCHANNEL) - private val mArrSampleIndex = IntArray(RXCHANNEL + STARTCHANNEL) - private val mArrSampleTemp = IntArray(RXCHANNEL + STARTCHANNEL) - private val mArrChannelTemp = IntArray(RXCHANNEL + STARTCHANNEL) - var mArrcntCheck = IntArray(RXCHANNEL + STARTCHANNEL) - - var mStartSampleCnt = 0 - - private val SENSOR_LIMIT_COUNT = 2000 - - init { - recorder = AudioRecorder(SAMPLERATE) - //INIT DYNAMIC REFERENCE ARRAY.. - for (i in 0 until RXCHANNEL + STARTCHANNEL) - mDynamicRefArray[i] = DEFAULT_REF - - isRecording = false - } - - - fun StartFFT() { - if (isRecording != true) { - recorder.start() - isRecording = true - } - recorder.read(samples, fftsize) - FFT.spectrum(samples, spectrum) - //FFT.spectrum_for_phase(samples, spectrum_p); - //FFT.getRealPart(real); - //FFT.getImagPart(image); - } - - fun StartFFT(windowsNum: Short) { - if (isRecording != true) { - recorder.start() - isRecording = true - } - recorder.read(samples, fftsize, windowsNum) - FFT.spectrum(samples, spectrum) - } - - fun DestroyFFT() { - FFT.dispose() - recorder.stop() - isRecording = false - } - - fun euDetectFreq(fFrequency: Int): Int { - - val fFreqRatio: Double - val freqIndex: Int - val fmax: Float - - fFreqRatio = fFrequency / MAXFREQUENCY - freqIndex = (fFreqRatio * fftsize / 2).toInt() + 1 - - //f1 = spectrum.get(freqIndex-1); - fmax = spectrum.get(freqIndex) - //f3 = spectrum_p.get(freqIndex); - //r1 = real.get(freqIndex); - //i1 = image.get(freqIndex); - /* - String s = ""; - for(int i = freqIndex - 1; i <= freqIndex + 1; i++) - s += " " + spectrum.get(i); - Log.i("HELLO FREQ", freqIndex + "::" + s); - */ - //f3 = spectrum.get(freqIndex+1); - - //if( fmax < f2 ) fmax = f2; - //if( fmax < f3 ) fmax = f3; - - return (fmax * 100000).toInt() - } - - fun euCatchSingleData() { - // UPDATED ON 1/2/2014 - // START BIT's Frequency Detection - mSampleTemp = this.euDetectFreq(START_BIT) - mMaxIndex = RXCHANNEL - // START BIT's Dynamic Reference Catch - mDynamicRefArray[RXCHANNEL] = euGetDynamicReference(mSampleTemp, RXCHANNEL) - - // Rest of frequency processing - for (i in 0 until RXCHANNEL) { - var currentFreq = this.euDetectFreq(START_FREQ + mFreqSpan * i) - mDynamicRefArray[i] = euGetDynamicReference(currentFreq, i) - - if (currentFreq >= mSampleTemp && currentFreq >= mDynamicRefArray[i]) { - mSampleTemp = currentFreq - mMaxIndex = i - } - } - - // BEFORE VERSION - /*for(int i = 0; i < RXCHANNEL + STARTCHANNEL ; i++) - { - if(i != RXCHANNEL) - currentFreq = (int) this.detectFreq(START_FREQ + mFreqSpan*i); - else - currentFreq = (int) this.detectFreq(START_BIT); - mDynamicRefArray[i] = getDynamicReference(currentFreq, i); - - if(currentFreq>=mSampleTemp && currentFreq >=mDynamicRefArray[i]){ - mSampleTemp = currentFreq; - mMaxIndex = i; - } - }*/ - - if (mMaxIndex != -1) { - mFreqArray[mMaxIndex]++ - } - if (mSampleIndex < 7) { - mSampleIndex++ - } else { - for (i in 0 until RXCHANNEL + STARTCHANNEL) { - if (mFreqArray[i] > mChannelTemp) { - mChannelTemp = mFreqArray[i] - mMaxIndex = i - } - //mbFreqArray[i] = 0; - } - - if (mChannelTemp > 2 && mMaxIndex != -1) { - if (mMaxIndex == 16) { - if (mChannelArrayList.size > 1) { - cntCheck++ - Log.v("mChanelArrayList.size()", "mChanelArrayList.size():" + mChannelArrayList.size) - - receivedData = "" - val a = IntArray(mChannelArrayList.size - 2) - for (i in 0 until mChannelArrayList.size - 2) { - a[i] = mChannelArrayList[i] - if (a[i] > 9) - receivedData += "" + ('a'.toInt() + (a[i] - 10)).toChar() - else - receivedData += "" + a[i] - } - - if (PacketErrorDetector.makeCheckSum(a) == mChannelArrayList[mChannelArrayList.size - 2] && PacketErrorDetector.makeParellelParity(a) == mChannelArrayList[mChannelArrayList.size - 1]) { - mStartSwt = false - isCompleted = true - receiveStr = EuDataDecoder.decodeStaticHexCharSource(a) - } - mChannelArrayList.clear() - } - } else { - mChannelArrayList.add(mMaxIndex) - } - Log.v("DATA", "DATAIndex : " + mMaxIndex + " mCheckedByte :" + mSampleIndex + " tempxcnt :" + mChannelTemp + " Data :" + this.euDetectFreq(START_FREQ + mFreqSpan * mMaxIndex)) - Log.v("DaTA", "0:" + mFreqArray[0] + " 1:" + mFreqArray[1] + " 2:" + mFreqArray[2] + " 3:" + mFreqArray[3] + " 4:" + mFreqArray[4] + " 5:" + mFreqArray[5] + " 6:" + mFreqArray[6] + " 7:" + mFreqArray[7] + " 8:" + mFreqArray[8] - + " 9:" + mFreqArray[9] + " 10:" + mFreqArray[10] + " 11:" + mFreqArray[11] + " 12:" + mFreqArray[12] + " 13:" + mFreqArray[13] + " 14:" + mFreqArray[14] + " 15:" + mFreqArray[15] + " 16:" + mFreqArray[16]) - } else { - mChannelArrayList.add(-1) - Log.v("DATAX", "DATAIndex : " + mMaxIndex + " mCheckedByte :" + mSampleIndex + " Data :" + this.euDetectFreq(START_FREQ + mFreqSpan * mMaxIndex)) - Log.v("DaTA", "0:" + mFreqArray[0] + " 1:" + mFreqArray[1] + " 2:" + mFreqArray[2] + " 3:" + mFreqArray[3] + " 4:" + mFreqArray[4] + " 5:" + mFreqArray[5] + " 6:" + mFreqArray[6] + " 7:" + mFreqArray[7] + " 8:" + mFreqArray[8] - + " 9:" + mFreqArray[9] + " 10:" + mFreqArray[10] + " 11:" + mFreqArray[11] + " 12:" + mFreqArray[12] + " 13:" + mFreqArray[13] + " 14:" + mFreqArray[14] + " 15:" + mFreqArray[15] + " 16:" + mFreqArray[16]) - - } - mMaxIndex = -1 - mSampleIndex = 0 - mSampleTemp = 0 - mChannelTemp = 0 - for (i in 0 until RXCHANNEL + STARTCHANNEL) {//// - mFreqArray[i] = 0//// - }///// - } - - } - - fun euCatchMultiData() { - for (j in 0 until RXCHANNEL + STARTCHANNEL) { - mArrMaxIndex[j] = -1 - mArrSampleIndex[j] = 0 - mArrSampleTemp[j] = 0 - mArrChannelTemp[j] = 0 - mArrcntCheck[j] = 0 - mFreqArray[j] = 0 - } - val arrCurrentFreq = IntArray(RXCHANNEL + STARTCHANNEL) - for (i in 0 until RXCHANNEL + STARTCHANNEL) { - arrCurrentFreq[i] = this.euDetectFreq(START_FREQ + mFreqSpan * i) - if (arrCurrentFreq[i] >= 200) { - //mArrSampleTemp[i] = arrCurrentFreq[i]; - mArrMaxIndex[i] = i - } - } - } - - fun getmMaxIndex(): Int { - return mMaxIndex - } - - fun setmMaxIndex(mMaxIndex: Int) { - this.mMaxIndex = mMaxIndex - } - - fun getmArrMaxIndex(): IntArray { - return mArrMaxIndex - } - - fun setmArrMaxIndex(mArrMaxIndex: IntArray) { - this.mArrMaxIndex = mArrMaxIndex - } - - fun euCheckStartPoint(): Boolean? { - var tempFreq = 50 - var tempIndex = -1 - - for (i in -1..1) { - var currentFreq = this.euDetectFreq(START_BIT + mFreqSpan * i) - if (currentFreq > tempFreq) { - tempFreq = currentFreq - tempIndex = 0 - } - } - if (tempIndex == 0) { - if (mStartSampleCnt == 8) - return true - else - mStartSampleCnt++ - } else - mStartSampleCnt = 0 - - return false - } - - private fun euSpecificFreqSensor(_freq: Int): Boolean { - var dynRef = 500 - val dynRefCnt = 0 - var criticalCnt = 0 - var limitCnt = 0 - var temp = 0 - val preservedRef = 0 - while (SENSOR_LIMIT_COUNT > limitCnt++) { - this.StartFFT() - - val curFreq = this.euDetectFreq(_freq) - dynRef = euGetDynamicRef(curFreq, dynRef, preservedRef, dynRefCnt) - - if (curFreq >= dynRef) { - val prevFreq = this.euDetectFreq(_freq - mFreqSpan) - val nextFreq = this.euDetectFreq(_freq + mFreqSpan) - - if (prevFreq > curFreq || nextFreq > curFreq) - continue - else { - criticalCnt++ - temp = limitCnt - } - } - - if (criticalCnt > 4) { - if (limitCnt - temp > 12) { - criticalCnt = 0 - continue - } else - return true - } - } - return false - } - - fun euGetDynamicRef(curFreq: Int, dynRef: Int, preservedRef: Int, dynRefCnt: Int): Int { - var dynRef = dynRef - - if (curFreq > dynRef) - // DATA TRUE - { - when (dynRefCnt) { - 0 -> dynRef = curFreq - 1 -> dynRef = (curFreq + dynRef) / 3 - 2 -> dynRef = preservedRef - } - } else - //DATA FALSE - { - dynRef -= ((dynRef - curFreq) * 0.9).toInt() - } - - // MAXIMUM and MIMINUM DATA CATCHING - if (dynRef > MAXREFERENCE) - dynRef = MAXREFERENCE - if (dynRef < MINREFERENCE) - dynRef = MINREFERENCE - - return dynRef - } - - fun euGetDynamicReference(nfreq: Int, freqIndex: Int): Int { - if (nfreq > mDynamicRefArray[freqIndex]) - // DATA TRUE - { - when (mRefCntIndexArray[freqIndex]) { - 0 -> { - mDynamicRefArray[freqIndex] = nfreq - mRefCntIndexArray[freqIndex]++ - } - 1 -> { - mDynamicRefArray[freqIndex] = (nfreq + mDynamicRefArray[freqIndex]) / 2 - mTempRef[freqIndex] = mDynamicRefArray[freqIndex] - mRefCntIndexArray[freqIndex]++ - } - 2 -> mDynamicRefArray[freqIndex] = mTempRef[freqIndex] - } - } else - //DATA FALSE - { - mDynamicRefArray[freqIndex] -= ((mDynamicRefArray[freqIndex] - nfreq) * 0.9).toInt() - mRefCntIndexArray[freqIndex] = 0 - } - - // MAXIMUM and MIMINUM DATA CATCHING - if (mDynamicRefArray[freqIndex] > MAXREFERENCE) - mDynamicRefArray[freqIndex] = MAXREFERENCE - if (mDynamicRefArray[freqIndex] < MINREFERENCE) - mDynamicRefArray[freqIndex] = MINREFERENCE - - return mDynamicRefArray[freqIndex] - } - - - private fun allocateByteBuffer(numSamples: Int): ByteBuffer { - val buffer = ByteBuffer.allocateDirect(numSamples * 2) - buffer.order(ByteOrder.nativeOrder()) - return buffer - } - - private fun allocateFloatBuffer(numSamples: Int): FloatBuffer { - val buffer = allocateByteBuffer(numSamples * 2) - return buffer.asFloatBuffer() - } - - private fun allocateShortBuffer(numSamples: Int): ShortBuffer { - val buffer = allocateByteBuffer(numSamples * 2) - return buffer.asShortBuffer() - } - - companion object { - var receiveStr = "" - } - -} diff --git a/euphony/src/main/kotlin/euphony/lib/receiver/EuRxManager.kt b/euphony/src/main/kotlin/euphony/lib/receiver/EuRxManager.kt deleted file mode 100644 index 46d6b73..0000000 --- a/euphony/src/main/kotlin/euphony/lib/receiver/EuRxManager.kt +++ /dev/null @@ -1,208 +0,0 @@ -package euphony.lib.receiver - -import android.os.Handler -import android.os.Message -import android.util.Log -import euphony.lib.util.COMMON - -class EuRxManager { - private var mRxThread: Thread? = null - private var mRxRunner: RxRunner? = null - private var mPsRunner: PsRunner? = null - private var mPsThread: Thread? = null - private var _active: Boolean = false - - private var mHex = false - - var acousticSensor: AcousticSensor? = null - - private val mHandler = object : Handler() { - override fun handleMessage(msg: Message) { - when (msg.what) { - RX_DECODE -> acousticSensor!!.notify(msg.obj.toString() + "") - else -> { - } - } - } - } - - var positionDetector: PositionDetector? = null - - private val mPsHandler = object : Handler() { - override fun handleMessage(msg: Message) { - when (msg.what) { - PS_DECODE -> positionDetector!!.detectSignal(msg.obj as Int) - } - } - } - - constructor() {} - constructor(hex: Boolean) { - mHex = hex - } - - @JvmOverloads - fun listen(hex: Boolean = mHex) { - _active = true - mRxRunner = RxRunner(hex) - mRxThread = Thread(mRxRunner, "RX") - mRxThread!!.start() - } - - fun find() { - _active = true - mPsRunner = PsRunner() - mPsThread = Thread(mPsRunner, "PS") - mPsThread!!.start() - } - - fun finishToFind() { - if (mPsThread != null) { - _active = false - while (true) { - try { - mPsThread!!.join() - break - } catch (e: InterruptedException) { - Log.i("FINISH", e.message) - } - - } - } - - if (mPsRunner != null) - mPsRunner!!.DestroyFFT() - - mPsThread = null - mPsRunner = null - } - - fun finish() { - if (mRxThread != null) { - _active = false - while (true) { - try { - mRxThread!!.join() - break - } catch (e: InterruptedException) { - Log.i("FINISH", e.message) - } - - } - } - if (mRxRunner != null) - mRxRunner!!.DestroyFFT() - - mRxThread = null - mRxRunner = null - } - - private inner class RxRunner : EuFreqObject, Runnable { - internal var mHex = false - - internal constructor() {} - internal constructor(hex: Boolean) { - mHex = hex - } - - override fun run() { - while (_active) { - StartFFT() - if (mStartSwt!!) - euCatchSingleData() - else - mStartSwt = euCheckStartPoint() - - if (isCompleted!!) { - val msg = mHandler.obtainMessage() - msg.what = RX_DECODE - msg.obj = if (mHex) receivedData else EuDataDecoder.decodeStaticHexCharSource(receivedData) - isCompleted = false - mHandler.sendMessage(msg) - mRxRunner!!.DestroyFFT() - return - } - } - } - } - - private inner class PsRunner : EuFreqObject(), Runnable { - - override fun run() { - var startswt = false - var startcnt = 0 - var specificFreq = 0 - Log.i("START", "START LISTEN") - while (_active) { - //To find the frequency point - while (!startswt) { - StartFFT() - var i: Int - i = 21000 - while (i >= 16500) { - if (100 < euDetectFreq(i)) { - startswt = true - break - } - i -= COMMON.CHANNEL_SPAN - } - specificFreq = i - - //there is no af area.. - if (startcnt++ > 1000) { - _active = false - startswt = true - Log.i("START", "FAILED to find any position") - } - } - - var signal: Int - var max_signal = 0 - var avr_signal = 0 - var noSignalCnt = 0 - var processingCnt = 0 - var maxCnt = 0 - do { - StartFFT() - signal = euDetectFreq(specificFreq) - - if (signal < 20) - noSignalCnt++ - else { - noSignalCnt = 0 - - if (max_signal < signal) { - maxCnt++ - max_signal = signal - avr_signal += max_signal - } - if (++processingCnt > 50) { - avr_signal /= maxCnt - val msg = mPsHandler.obtainMessage() - msg.what = PS_DECODE - msg.obj = avr_signal - mPsHandler.sendMessage(msg) - processingCnt = 0 - max_signal = 0 - avr_signal = 0 - maxCnt = 0 - } - } - } while (noSignalCnt < 50 && _active) - - val msg = mPsHandler.obtainMessage() - msg.what = PS_DECODE - msg.obj = -1 - mPsHandler.sendMessage(msg) - break - - } - } - } - - companion object { - - private val RX_DECODE = 1 - private val PS_DECODE = 2 - } -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/receiver/EuWindows.kt b/euphony/src/main/kotlin/euphony/lib/receiver/EuWindows.kt deleted file mode 100644 index b06fd31..0000000 --- a/euphony/src/main/kotlin/euphony/lib/receiver/EuWindows.kt +++ /dev/null @@ -1,103 +0,0 @@ -package euphony.lib.receiver - -import java.nio.ByteBuffer - -class EuWindows(mWindowNumber: Short, buffer: ByteBuffer, bufferSize: Int) { - var windowNumber: Short = 0 - - var buffer: ByteBuffer? = null - var bufferSize = 0 - private val mRawData: ByteArray - var kaiserAlph = 32.0 - var kaiserWindowSize = 45.0 - var hammingAlph = 54.0 - var blackmanAlph = 0.16 - - init { - this.buffer = buffer - this.bufferSize = bufferSize - mRawData = buffer.array() - windowNumber = mWindowNumber - } - - internal fun Processor() { - if (buffer != null && bufferSize != 0) - // check buffer buffersize - { - when (windowNumber as Int) { - RECTANGULAR -> RectangularWindow() - TRIANGLAR -> TrianglarWindow() - HANNING -> HanningWindow() - HAMMING -> HammingWindow() - BLACKMAN -> BlackmanWindow() - KAISER -> KaiserWindow() - } - } - } - - internal fun RectangularWindow() { - for (i in mRawData.indices) - mRawData[i] = (mRawData[i] * 1.0).toByte() - buffer = ByteBuffer.wrap(mRawData) - - } - - internal fun TrianglarWindow() { - for (i in mRawData.indices) - mRawData[i] = (mRawData[i] * ((mRawData.size / 2.0 - Math.abs(i - (mRawData.size - 1.0) / 2.0)) / (mRawData.size / 2.0))).toByte() - buffer = ByteBuffer.wrap(mRawData) - } - - internal fun HanningWindow() { - for (i in mRawData.indices) - mRawData[i] = (mRawData[i] * (0.5 * (1 - Math.cos(2.0 * Math.PI * i.toDouble() / (512 - 1))))).toByte() - buffer = ByteBuffer.wrap(mRawData) - } - - internal fun HammingWindow() { - for (i in mRawData.indices) - mRawData[i] = (mRawData[i] * (hammingAlph + (1.0 - hammingAlph) * Math.cos(2.0 * Math.PI * i.toDouble() / mRawData.size + Math.PI))).toByte() - buffer = ByteBuffer.wrap(mRawData) - } - - internal fun BlackmanWindow() { - for (i in mRawData.indices) - mRawData[i] = (mRawData[i] * ((1.0 - blackmanAlph) / 2.0 - 0.5 * Math.cos(2.0 * Math.PI * i.toDouble() / (mRawData.size - 1.0)) + blackmanAlph / 2.0 * Math.cos(4.0 * Math.PI * i.toDouble() / (mRawData.size - 1.0)))).toByte() - buffer = ByteBuffer.wrap(mRawData) - } - - internal fun KaiserWindow() { - val n = kaiserWindowSize / 2.0 - for (i in mRawData.indices) { - mRawData[i] = (mRawData[i] * (Util.I0(kaiserAlph * Math.sqrt(1.0 - Math.pow(2.0 * (i.toDouble() - n) / kaiserWindowSize, 2.0))) / Util.I0(kaiserAlph))).toByte() - } - buffer = ByteBuffer.wrap(mRawData) - } - - internal object Util { - fun I0(x: Double): Double { - val eps = 10E-9 - var n = 1 - var S = 1.0 - var D = 1.0 - while (D > eps * S) { - val T = x / (2.0 * n) - n++ - D *= T * T - S += D - } - return S - } - } - - companion object { - - //windows - val RECTANGULAR = 0 - val TRIANGLAR = 1 - val HANNING = 2 - val HAMMING = 3 - val BLACKMAN = 4 - val KAISER = 5 - } -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/receiver/KissFFT.kt b/euphony/src/main/kotlin/euphony/lib/receiver/KissFFT.kt deleted file mode 100644 index 047b1e0..0000000 --- a/euphony/src/main/kotlin/euphony/lib/receiver/KissFFT.kt +++ /dev/null @@ -1,117 +0,0 @@ -package euphony.lib.receiver - -import android.util.Log -import java.nio.ByteBuffer -import java.nio.FloatBuffer -import java.nio.ShortBuffer - -class KissFFT(numSamples: Int) { - - /** the pointer to the kiss fft object */ - private val handle: Long - - init { - handle = create(numSamples) - Log.i("var", " $handle") - } - - /** - * Creates a new kiss fft object - * - * @param timeSize - * the number of samples - * @return the handle to the kiss fft object - */ - private external fun create(timeSize: Int): Long - - /** - * Destroys a kiss fft object - * - * @param handle - * the handle to the kiss fft object - */ - private external fun destroy(handle: Long) - - /** - * Calculates the frequency spectrum of the given samples. There must be as - * many samples as specified in the constructor of this class. Spectrum must - * hold timeSize / 2 + 1 elements - * - * @param handle - * the handle to the kiss fft object - * @param samples - * the samples in 16-bit signed PCM encoding - * @param spectrum - * the spectrum - */ - private external fun spectrum(handle: Long, samples: ShortBuffer, - spectrum: FloatBuffer) - - /** - * Calculates the frequency spectrum of the given samples. There must be as - * many samples as specified in the constructor of this class. Spectrum must - * hold timeSize / 2 + 1 elements - * - * @param samples - * the samples - * @param spectrum - * the spectrum - */ - fun spectrum(samples: ShortBuffer, spectrum: FloatBuffer) { - spectrum(handle, samples, spectrum) - } - - /** - * Calculates the frequency spectrum of the given samples. There must be as - * many samples as specified in the constructor of this class. Spectrum must - * hold timeSize / 2 + 1 elements - * - * @param samples - * the samples - * @param spectrum - * the spectrum - */ - fun spectrum(samples: ByteBuffer, spectrum: FloatBuffer) { - spectrum(samples.asShortBuffer(), spectrum) - } - - private external fun spectrum_for_phase(handle: Long, samples: ShortBuffer, specturm: FloatBuffer) - - fun spectrum_for_phase(samples: ShortBuffer, spectrum: FloatBuffer) { - spectrum_for_phase(handle, samples, spectrum) - } - - fun spectrum_for_phase(samples: ByteBuffer, spectrum: FloatBuffer) { - spectrum_for_phase(samples.asShortBuffer(), spectrum) - } - - /** - * Releases all resources of this object - */ - fun dispose() { - destroy(handle) - } - - fun getRealPart(real: ShortBuffer) { - getRealPart(handle, real) - } - - fun getImagPart(imag: ShortBuffer) { - getImagPart(handle, imag) - } - - private external fun getRealPart(handle: Long, real: ShortBuffer) - - private external fun getImagPart(handle: Long, imag: ShortBuffer) - - companion object { - - init { - System.loadLibrary("kissff") // kiss_fft.c - System.loadLibrary("kissfftr") // kiss_fftr.c - System.loadLibrary("kissfft") // KissFFT.cpp - } - } - - -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/receiver/PositionDetector.kt b/euphony/src/main/kotlin/euphony/lib/receiver/PositionDetector.kt deleted file mode 100644 index 5c2ccbd..0000000 --- a/euphony/src/main/kotlin/euphony/lib/receiver/PositionDetector.kt +++ /dev/null @@ -1,6 +0,0 @@ -package euphony.lib.receiver - -interface PositionDetector { - fun detectSignal(signal: Int) - fun detectFq(fq: Int) -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/transmitter/EuCodeMaker.kt b/euphony/src/main/kotlin/euphony/lib/transmitter/EuCodeMaker.kt deleted file mode 100644 index 2522898..0000000 --- a/euphony/src/main/kotlin/euphony/lib/transmitter/EuCodeMaker.kt +++ /dev/null @@ -1,71 +0,0 @@ -package euphony.lib.transmitter - -import android.util.Log -import euphony.lib.util.PacketErrorDetector - -class EuCodeMaker : EuFreqGenerator { - internal var mMainString: String? = null - internal var mCodeSource: ShortArray? = null - var START_BIT = freqBasePoint - freqSpan - - private var mChannelMode = CHANNEL.EXINGLE - - enum class CHANNEL { - SINGLE, MULTI, EXINGLE - } - - constructor() {} - - constructor(channelMode: CHANNEL) { - mChannelMode = channelMode - } - - - fun euAssembleData(data: String): ShortArray { - var assembledData = euApplyCrossFade(euMakeStaticFrequency(START_BIT, 0)) - val payload = IntArray(data.length + 1) - - when (mChannelMode) { - EuCodeMaker.CHANNEL.SINGLE -> for (i in 0 until data.length) { - when (data[i]) { - '0' -> assembledData = euAppendRawData(assembledData, zeroSource) - '1' -> assembledData = euAppendRawData(assembledData, euApplyCrossFade(euMakeStaticFrequency(freqBasePoint, 0))) - } - } - - EuCodeMaker.CHANNEL.MULTI -> for (i in 0 until data.length) { - when (data[i]) { - '0' -> { - } - '1' -> assembledData = euMixingRawData(assembledData, euApplyCrossFade(euMakeStaticFrequency(freqBasePoint + freqSpan * (i + 1), 0))) - } - } - - EuCodeMaker.CHANNEL.EXINGLE -> for (i in 0 until data.length) { - val ch = data[i] - when (ch) { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> assembledData = euAppendRawData(assembledData, euApplyCrossFade(euMakeStaticFrequency(freqBasePoint + freqSpan * (ch - '0'), 0))) - 'a', 'b', 'c', 'd', 'e', 'f' -> assembledData = euAppendRawData(assembledData, euApplyCrossFade(euMakeStaticFrequency(freqBasePoint + freqSpan * (ch - 'a' + 10), 0))) - } - } - } - - for (i in 0 until data.length) { - when (data[i]) { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> payload[i] = data[i] - '0' - 'a', 'b', 'c', 'd', 'e', 'f' -> payload[i] = data[i] - 'a' + 10 - } - } - val checksum = PacketErrorDetector.makeCheckSum(payload) - val parity = PacketErrorDetector.makeParellelParity(payload) - - Log.i("UHEHE", "CHECKSUM : $checksum PARITY : $parity") - assembledData = euAppendRawData(assembledData, euApplyCrossFade(euMakeStaticFrequency(freqBasePoint + freqSpan * checksum, 0))) - assembledData = euAppendRawData(assembledData, euApplyCrossFade(euMakeStaticFrequency(freqBasePoint + freqSpan * parity, 0))) - - assembledData = euAppendRawData(assembledData, euApplyCrossFade(euMakeStaticFrequency(START_BIT, 0))) - - return assembledData - } - -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/transmitter/EuDataEncoder.kt b/euphony/src/main/kotlin/euphony/lib/transmitter/EuDataEncoder.kt deleted file mode 100644 index 9f9a5f0..0000000 --- a/euphony/src/main/kotlin/euphony/lib/transmitter/EuDataEncoder.kt +++ /dev/null @@ -1,63 +0,0 @@ -package euphony.lib.transmitter - -import android.util.Log -import euphony.lib.util.EuCodec -import java.io.ByteArrayOutputStream -import java.io.DataOutputStream -import java.io.IOException - -class EuDataEncoder : EuCodec { - var originalSource: String? = null - - internal constructor() {} - constructor(_source: String) { - originalSource = _source - } - - fun encodeHexCharSource(): String { - return encodeStaticHexCharSource(originalSource) - } - - fun encodeStaticHexCharSource(_source: String?): String { - val strBuilder = StringBuilder() - - for (i in 0 until _source!!.length) { - val data = _source[i].toInt() - strBuilder.append(Integer.toHexString(data)) - } - - return strBuilder.toString() - } - - companion object { - - fun base40StaticEncode(_source: String): ByteArray { - try { - val baos = ByteArrayOutputStream() - val dos = DataOutputStream(baos) - var i = 0 - while (i < _source.length) { - when (Math.min(3, _source.length - i)) { - 1 -> { - val b = EuCodec.base40Index[_source[i] as Int] - dos.writeByte(b.toInt()) - } - 2 -> { - val ch = (EuCodec.base40Index[_source[i + 1] as Int] * 40 + EuCodec.base40Index[_source[i] as Int]).toChar() - dos.writeChar(ch.toInt()) - } - 3 -> { - val ch2 = ((EuCodec.base40Index[_source[i + 2] as Int] * 40 + EuCodec.base40Index[_source[i + 1] as Int]).toChar().toInt() * 40 + EuCodec.base40Index[_source[i] as Int]).toChar() - dos.writeChar(ch2.toInt()) - } - } - i += 3 - } - return baos.toByteArray() - } catch (e: IOException) { - throw AssertionError(e) - } - - } - } -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/transmitter/EuFreqGenerator.kt b/euphony/src/main/kotlin/euphony/lib/transmitter/EuFreqGenerator.kt deleted file mode 100644 index bb3ec02..0000000 --- a/euphony/src/main/kotlin/euphony/lib/transmitter/EuFreqGenerator.kt +++ /dev/null @@ -1,134 +0,0 @@ -package euphony.lib.transmitter - -import euphony.lib.util.COMMON - -open class EuFreqGenerator { - - // FIXED ACOUSTIC DATA - val SAMPLERATE = COMMON.SAMPLERATE//44100; - val DATA_LENGTH = COMMON.DATA_LENGTH//2048; - val PI = Math.PI - val PI2 = PI * 2 - - // Member for Frequency point - // DEFAULT DEFINITION - var freqBasePoint = COMMON.START_FREQ - var freqSpan = COMMON.CHANNEL_SPAN //86 - val zeroSource = ShortArray(DATA_LENGTH) - - constructor() {} - - constructor(freqStartPoint: Int, freqSpan: Int) { - this.freqSpan = freqSpan - freqBasePoint = freqStartPoint - } - - fun euMakeStaticFrequency(freq: Int, degree: Int): ShortArray { - val double_source = DoubleArray(DATA_LENGTH) - val source = ShortArray(DATA_LENGTH) - var time: Double - val phase: Double - - for (i in 0 until DATA_LENGTH) { - time = i.toDouble() / SAMPLERATE.toDouble() - double_source[i] = Math.sin(PI2 * freq.toDouble() * time) - source[i] = (32767 * double_source[i]).toShort() - } - - return source - } - - fun euMakeFrequency(source: ShortArray, freq: Int) { - var source = source - source = euMakeStaticFrequency(freq, 0) - } - - //updated - fun euMakeFrequencyWithCrossFade(freq: Int): ShortArray { - return euApplyCrossFade(euMakeStaticFrequency(freq, 0)) - } - - fun euApplyCrossFade(source: ShortArray): ShortArray { - var mini_window: Double - val fade_section = COMMON.FADE_RANGE - for (i in 0 until fade_section) { - mini_window = i.toDouble() / fade_section.toDouble() - source[i] = (source[i].toDouble() * mini_window).toShort() - source[DATA_LENGTH - 1 - i] = (source[DATA_LENGTH - 1 - i].toDouble() * mini_window).toShort() - } - - return source - } - - fun euAppendRawData(src: ShortArray?, objective: ShortArray): ShortArray { - val SRC_LENGTH: Int - val TOTAL_LENGTH: Int - if (src == null) - SRC_LENGTH = 0 - else - SRC_LENGTH = src.size - TOTAL_LENGTH = SRC_LENGTH + objective.size - - val dest = ShortArray(TOTAL_LENGTH) - - for (i in 0 until SRC_LENGTH) - dest[i] = src!![i] - for (i in SRC_LENGTH until TOTAL_LENGTH) - dest[i] = objective[i - SRC_LENGTH] - - return dest - } - - fun euLinkRawData(vararg sources: ShortArray): ShortArray { - val dest = ShortArray(sources.size * DATA_LENGTH) - - for (i in sources.indices) - for (j in 0 until sources[i].size) - dest[j + i * DATA_LENGTH] = sources[i][j] - - return dest - } - - fun euLinkRawData(isCrossfaded: Boolean, vararg sources: ShortArray): ShortArray { - val dest = ShortArray(sources.size * DATA_LENGTH) - for (i in sources.indices) { - var src = sources[i] - if (isCrossfaded) - src = euApplyCrossFade(sources[i]) - - for (j in 0 until src.size) - dest[j + i * DATA_LENGTH] = src[j] - } - - return dest - } - - fun euMixingRawData(vararg sources: ShortArray): ShortArray { - val dest = sources[0].clone() - - for (i in 1 until sources.size) { - for (j in 0 until sources[i].size) { - dest[j] = ((dest[j].toInt() + sources[i][j].toInt()) / 2).toShort() - } - } - return dest - } - - fun euMakeMaximumVolume(source: ShortArray): ShortArray { - var max = 0 - //SCAN FOR VOLUME UP - for (i1 in source) - if (max < Math.abs(i1.toInt())) - max = i1.toInt() - if (32767 == max) - return source - - - for (i in source.indices) - source[i] = ( source[i] * (32767.0 / max.toDouble())).toShort() - - return source - } - - -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/transmitter/EuPlayer.kt b/euphony/src/main/kotlin/euphony/lib/transmitter/EuPlayer.kt deleted file mode 100644 index cb3bc5a..0000000 --- a/euphony/src/main/kotlin/euphony/lib/transmitter/EuPlayer.kt +++ /dev/null @@ -1,38 +0,0 @@ -package euphony.lib.transmitter - -import android.media.AudioAttributes -import android.media.AudioFormat -import android.media.AudioManager -import android.media.AudioTrack -import euphony.lib.util.COMMON - -class EuPlayer { - - private var mSource: ShortArray? = null - private var mAudioTrack: AudioTrack? = null - private val DATA_LENGTH = COMMON.FFT_SIZE * 4//2048; - private val mZeroSource = ShortArray(DATA_LENGTH) - - constructor() {} - - constructor(src: ShortArray) { - this.setSource(src) - } - - fun setSource(src: ShortArray) { - mSource = src - mAudioTrack = AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, src.size * 2, AudioTrack.MODE_STATIC) - } - - fun play() { - mAudioTrack!!.write(mSource!!, 0, mSource!!.size) - mAudioTrack!!.setLoopPoints(0, mSource!!.size, -1) - mAudioTrack!!.play() - } - - fun stop() { - if (mAudioTrack != null) - if (mAudioTrack!!.playState == AudioTrack.PLAYSTATE_PLAYING) - mAudioTrack!!.pause() - } -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/transmitter/EuTxManager.kt b/euphony/src/main/kotlin/euphony/lib/transmitter/EuTxManager.kt deleted file mode 100644 index 43b8184..0000000 --- a/euphony/src/main/kotlin/euphony/lib/transmitter/EuTxManager.kt +++ /dev/null @@ -1,63 +0,0 @@ -package euphony.lib.transmitter - -import android.content.Context -import android.media.AudioFormat -import android.media.AudioManager -import android.media.AudioTrack -import android.util.Log - -class EuTxManager { - private var mAudioTrack: AudioTrack? = null - private val mCodeMaker = EuCodeMaker() - private val mDataEncoder = EuDataEncoder() - - private var mHex = false - private var mOutStream: ShortArray? = null - - constructor() {} - - constructor(hex: Boolean) { - mHex = hex - } - - fun euInitTransmit(data: String) { - mOutStream = mCodeMaker.euAssembleData(if (mHex) data else mDataEncoder.encodeStaticHexCharSource(data)) - } - - @JvmOverloads - fun process(count: Int = 1) { - var count = count - if (count > 0) - mAudioTrack = AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, mOutStream!!.size * 2, AudioTrack.MODE_STREAM) - else { - mAudioTrack = AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, mOutStream!!.size * 2, AudioTrack.MODE_STATIC) - count = -1 - } - - mAudioTrack!!.setLoopPoints(0, mOutStream!!.size, count) - - if (mAudioTrack != null) { - try { - mAudioTrack!!.write(mOutStream!!, 0, mOutStream!!.size) - mAudioTrack!!.play() - } catch (e: IllegalStateException) { - Log.i("PROCESS", e.message) - } - - } - } - - fun stop() { - if (mAudioTrack != null) - mAudioTrack!!.pause() - } - - fun setSystemVolumeMax(_context: Context) { - val am = _context.getSystemService(Context.AUDIO_SERVICE) as AudioManager - - am.setStreamVolume( - AudioManager.STREAM_MUSIC, - am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), - 0) - } -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/util/COMMON.kt b/euphony/src/main/kotlin/euphony/lib/util/COMMON.kt deleted file mode 100644 index 4fa95cc..0000000 --- a/euphony/src/main/kotlin/euphony/lib/util/COMMON.kt +++ /dev/null @@ -1,23 +0,0 @@ -package euphony.lib.util - -interface COMMON { - companion object { - // RX & TX COMMON VARIABLES - val SAMPLERATE = 44100 - val FFT_SIZE = 512 - val DATA_LENGTH = FFT_SIZE * 4 - val FADE_RANGE = DATA_LENGTH / 16 - - val MAX_FREQ = 22050.0 - val START_FREQ = 18000 - val CHANNEL = 16 - val CHANNEL_SPAN = SAMPLERATE / FFT_SIZE // Frequency Interval - val BUNDLE_INTERVAL = CHANNEL_SPAN * CHANNEL - - // RX - val MAX_REF = 4000 - val MIN_REF = 50 - - val DEFAULT_REF = 500 // BASE Reference value - } -} diff --git a/euphony/src/main/kotlin/euphony/lib/util/EuCodec.kt b/euphony/src/main/kotlin/euphony/lib/util/EuCodec.kt deleted file mode 100644 index dc49b90..0000000 --- a/euphony/src/main/kotlin/euphony/lib/util/EuCodec.kt +++ /dev/null @@ -1,32 +0,0 @@ -package euphony.lib.util; - -import java.util.Arrays; - -open class EuCodec { - private val chars = StringBuilder(BASE40) - - init { - chars.append('\u0000') - run { - var ch = 'a' - while (ch <= 'z') { - chars.append(ch) - ch++ - } - } - var ch = '0' - while (ch <= '9') { - chars.append(ch) - ch++ - } - chars.append("-:.") - Arrays.fill(base40Index, (-1).toByte()) - for (i in 0 until chars.length) - base40Index[chars[i] as Int] = i as Byte - } - - companion object { - private val BASE40 = 40 - var base40Index = ByteArray(256) - } -} \ No newline at end of file diff --git a/euphony/src/main/kotlin/euphony/lib/util/PacketErrorDetector.kt b/euphony/src/main/kotlin/euphony/lib/util/PacketErrorDetector.kt deleted file mode 100644 index b3c37d5..0000000 --- a/euphony/src/main/kotlin/euphony/lib/util/PacketErrorDetector.kt +++ /dev/null @@ -1,136 +0,0 @@ -package euphony.lib.util - -import android.util.Log - -class PacketErrorDetector { - - private var mEvenParity = false - - /***************************************************** - * This function sets EvenParity Bit's Value with parameter (force to set) - * - * parameter : - * int nParityValue - 0 : Data Bit Value 1's number is EVEN - * 1 : Data Bit Value 1's number is ODD - * - * - * return : none - */ - fun euSetEvenParityState(nParityState: Boolean) { - mEvenParity = nParityState - } - - /***************************************************** - * This function returns EvenParity Bit's Value. - * parameter : none - * return type : int - * 0 : Data Bit Value 1's number is EVEN - * 1 : Data Bit Value 1's number is ODD - */ - fun euGetEvenParityState(): Boolean { - return mEvenParity - } - - companion object { - - /***************************************************** - * This function checks ParityBit(Even) and judges payload data is reliable - * - * parameter : - * int[] Payload - Payload Data - * int nParity - Payrity bit's value - * return type : int - * TRUE - Parity Check Result means data is reliable - * FALSE - Parity Check Result means data is unreliable - */ - fun checkEvenParity(payload: IntArray, nParityBit: Int): Boolean { - var ntemp = 0 - for (i in payload.indices) { - ntemp = ntemp xor payload[i] - } - ntemp = ntemp xor nParityBit - return if (ntemp == 0) - // result of Parity Calculation is 0 --> Even Parity Correct! - true - else { // result of Parity Calculation is 1 --> Even Parity incorrect! - false - } - } - - /***************************************************** - * This function makes ParellelParityBit(int[] payLoad) (word : 4bit) - * - * parameter : - * int[] payLoad - Payload Data - * return type : int - * the EvenParity data to transmit - */ - fun makeParellelParity(payLoad: IntArray): Int { - var evenParity1 = 0 - var evenParity2 = 0 - var evenParity3 = 0 - var evenParity4 = 0 - val evenParity: Int - - for (i in payLoad.indices) { - evenParity1 += 0x8 and payLoad[i] shr 3 - evenParity2 += 0x4 and payLoad[i] shr 2 - evenParity3 += 0x2 and payLoad[i] shr 1 - evenParity4 += 0x1 and payLoad[i] - } - - Log.i("UHEHE", "$evenParity1 $evenParity2 $evenParity3 $evenParity4") - evenParity = (evenParity1 and 0x1) * 8 + (evenParity2 and 0x1) * 4 + (evenParity3 and 0x1) * 2 + (evenParity4 and 0x1) - - return evenParity - } - - /***************************************************** - * This function verify Checksum bit (word : 4bit) - * parameter : - * int[] Payload - Payload Data - * int nSizeOfPayload, - Payload Data's quantity - * int nCheckSum - Checksum bit's value - * return type : boolean - * true - Checksum is correct - * WAIT - Checksum is incorrect - */ - fun verifyCheckSum(payLoad: IntArray, checkSum: Int): Boolean { - var sumTemp = 0 - var checkResult = 0 - for (i in payLoad.indices) { // add all of payload data - sumTemp += payLoad[i] - } - // add checksum data - sumTemp += checkSum - // get 2's Complement and remove carry - checkResult = sumTemp.inv() + 1 and 0xF - return if (checkResult == 0x0) { - true - } else - false - } - - /***************************************************** - * This function makes Checksum bit(word : 4bit) - * parameter : int[] Payload - Payload Data - * int nSizeOfPayload, - Payload Data's quantity - * int nCheckSum - Checksum bit's value - * return type : int - * the Checksum data to transmit - */ - fun makeCheckSum(payLoad: IntArray): Int { - var nSumTemp = 0 - var nCheckSum = 0 // CheckSum's Initial value is 0 - for (i in payLoad.indices) { - nSumTemp += payLoad[i] - } - // remove carry - nSumTemp = nSumTemp and 0xF - // get 2's Complement and make checksum 4bit word - nCheckSum = nSumTemp.inv() + 1 and 0xF - Log.v("TCheckSum", "TCheckSum $nCheckSum") - return nCheckSum - } - } -} \ No newline at end of file diff --git a/euphony/src/main/libs/arm64-v8a/libkissff.so b/euphony/src/main/libs/arm64-v8a/libkissff.so deleted file mode 100755 index b6f6ac46203e6a598fe9faae181b4dc1b3d02059..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10144 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 KnW%F+&i;QK(NKT@ diff --git a/euphony/src/main/libs/arm64-v8a/libkissfft.so b/euphony/src/main/libs/arm64-v8a/libkissfft.so deleted file mode 100755 index 0040163f599f58b1b662e052d9f6e55e5fb76804..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10080 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 diff --git a/euphony/src/main/libs/arm64-v8a/libkissfftr.so b/euphony/src/main/libs/arm64-v8a/libkissfftr.so deleted file mode 100755 index 3fc8633e10b3f474d6a39aa5f473d5fabe5ee7c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10080 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 DR~F&< diff --git a/euphony/src/main/libs/armeabi-v7a/libkissff.so b/euphony/src/main/libs/armeabi-v7a/libkissff.so deleted file mode 100755 index 1a37070a1bc6cfdbdd9d5914827eb026e299c321..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22188 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 YCz1eWS$r^N+aiYpd?}GaWJbvU2>}P)YybcN diff --git a/euphony/src/main/libs/armeabi-v7a/libkissfft.so b/euphony/src/main/libs/armeabi-v7a/libkissfft.so deleted file mode 100755 index 5bea1fe161c3a9601c1ca7aef42fb4549b8b3557..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18092 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 diff --git a/euphony/src/main/libs/armeabi-v7a/libkissfftr.so b/euphony/src/main/libs/armeabi-v7a/libkissfftr.so deleted file mode 100755 index 86f9bff330eaedc686d7c90e80e66038bce3fd12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18092 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 diff --git a/euphony/src/main/obj/local/arm64-v8a/libkissff.so b/euphony/src/main/obj/local/arm64-v8a/libkissff.so deleted file mode 100755 index 62a97f33715a11edc5cfdaf8cfad98bf1a1505bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24824 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 diff --git a/euphony/src/main/obj/local/arm64-v8a/libkissfft.so b/euphony/src/main/obj/local/arm64-v8a/libkissfft.so deleted file mode 100755 index 6e2226902775f07f2713be1b715e0ed4065d976a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57208 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 D0=goJ diff --git a/euphony/src/main/obj/local/arm64-v8a/libkissfftr.so b/euphony/src/main/obj/local/arm64-v8a/libkissfftr.so deleted file mode 100755 index 04da5026878e5e0845254f817e41ec3755c0a37c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17936 zcmeHPdt6-Aoj-T(+{p|O7$8U@28KKgF@{$lBpP%eF9L+TP@|u=lX(FXV1~>L3HXRe zqNa_08q}_7Yn!NPE2(a?UHhp^Pg-^+>xOX-Cj1?Fh)e_I0n zf&_ft7beR8Y4EM=+^9LO9$rkq|4RaXoA95^8fBv&kk$GX$^S?W@MmNv_+!tny z-VqTFNk6B6Pn6HI3HWa&;4_EA-Q{#N`TWi{->qIrI>X*BUl>Ga1%HH{?pBAprPa~o z^tG|3Z9!ky%UXS*kfW(796=n;wzhyfLI$H)yu`=ab={E9?+%1mo6p6)H8mB60-R_I zG)v4aVY`H(u*W@bo+LbN9C3I(&ae~NbRj>;YLMqUf;$|N^7=gjmk=PDY&ggr)vv1N zy2gCvPQM!}pAZlR zWWVT=zYoNx-!&Sa#3yD;Q#cdDmqS2^KECaR{yi8D>94IEw)S4vXEWrz zm1k4mq$p%>LH3v~6ZG#uzZgUR0rbfj`Xp$>^}?6X|0C!bG4uzZt7GUtgLcKx=RxROs9JoJBp0={b6)8dq8!(H412F>}kgG-r_XsZjU*AW?wJ#s-bs6Wd{dU^T9tu zZ}cV8u@An>M&6$}rTtUsUzrC#vD#XDYku@tKBWgKE}QC)K=zpQ_4LKOY@^_{gn!8PD#x zX~?W(C8Mghn18VAa`PQZX2B~3yP1>qRT$XQXAJh?K?56x{KJz9Yy1TE&*JBI7k}Lv zSviby9byBl?=EA{di6hyeJ52``aFDdy`MnuX98yjAIxG;yIicVhE3b;n!)`Sa!^gY*zk$WfyzO3PVUJ}<5T-p^_cCx{r;+gM^iPUvEiOcUpyAP(>aqMcTn!BI zWZFPw{@aiv&KMf>&%DCrR{*suoc{gLJyq0G`EFiMG#DR^#O=X&rS{bs0r|L)HAKR#@K>h!*4PdV?kuO7U|zWRxm z499lv{9{A@+g_ya_2<9Qj65|rqERGHB#G| z)9oX3v+N@^$yvLzEvzre+Ln7FXQ1+DCf3LM!anpBHr1KS3O45|=9XNxt0mW#vMG0P zsoHm9rrKxZ;Yq558+3!C39%^0ekvsllwr=hD2 zcZxR9-8$Ejb01>s9oEeI5bsx#lUtRPy0)aO-R%a}y<1`3cbZuDK8hv7U|~$$ATc!y4{+27Wv2rt}<)gX+^1X!)7O<_xALh zKE$0<3_0i2^tvs}3^_$CE%~mlybbv_dwR*Oc^k0CNj_W2gnh{jd%E>HmN^qTCGZQ~ zI_K@mhM5Lyvel4Y=Q1Sk-egGK&GX-y⁣Yp={U!xplj;ftRH!g>|npvu-P6-DyVF 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 diff --git a/euphony/src/main/obj/local/arm64-v8a/objs/kissff/kiss_fft.o b/euphony/src/main/obj/local/arm64-v8a/objs/kissff/kiss_fft.o deleted file mode 100644 index a0a291ed603f78502f32673d8ba3c1a06cad7ea5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36632 zcmeI53wRaPwfOhUoH;qkAukg0;6QQ`UI8V6Bs{Djhevqi@J_I3tsyx%Au%C=BtW7( z0)#6Xt`b2@i>;{8N|avPt1W_UQy*x3v?|t1y=@Kn0L3SwfI|7NHM7?_lXcGN<=X!5 z|NH*m-QPDed;j*@Yp=cbo|!!}J2~r%W=~fYMNm-0FGVm#REVRSgK>`$^@t%-66ICc zG}zKcPAmO7E4>?{qWjWYo=Z>buYPcgxcL5{gk={R68C+zwC}!iWl8(K{C4ubGxPfG zyLd~=zKfgmPKQk_aI9Uo{R>SQ@wp~Oh|#uAO%ab(z*a3h=Q9;?>RiFbA1o2#{CGt? z{+JNSP{odOn&>(p#N&@b`ka>8h4}~iiO1I4#foyZA|vVBV)%=RqI7*MLAK5(eCn$QH>s~4dQf@wNlksV#|imo zw1zWUYFAHG+r+nToxHzC8;kyC>>4+g$h2^10&AzIt(? zqJGbSy>pdwJ@vK$<@G(ox+flcTJ02j-0{L9gZVn-?*`d-w`=>+7d@HX6L*6h_k$h6 zmUbhu-Gd-K73>+*J<*VX<8H9=ek~|->^q)|yPd0-y}RAB|G`a*E=<1FGp7x<9eu@T zS%0pq7qDf$?w>1Q%i0Nc!j^U5*jxcyR?hu#O3wXr2Z4MAZ0?-$I3LJQfz1cD zzeL04P&^lh!{%_Nb|eqCb;cyrW%NKF3rBT^NGs1!Jmnd`QN;?;r#z$QLACSPCh!~j zrNna)^_{hg&bQRmQ^HY`A#!fYaERp@TaIV8^nBZMu@{>zS|(q-RO;?7&u|P9oxQbggTM6qHwjH^k}V#WP%d_PL$e{tzj z>ib$|{^xKU9xW0-gJTnviOCY3I6kB2h~X!-n68(gt{yF|>*6if!a1=B&W%zyM@rya zS-9^)!-9S951PO1gIhco;auwM*2>;ESN0^xC?KoC`J?T-__XN!Let>9QMx7x_0$6-oc*XUub&Qz3V;Ozl65t ztR3KjYemGufbO4{Z#<9x*=@(Vc6T=h5 zXK$#E4sVe-u;J3B(tgkml>Z8@3p?GS^ZV=7T~wwJ%A~^f9oUwr{X33nDd4;GuD3z{ z9q6}rwSHZ%Ub^(X>or%$eQN(xZ>Xsqzx~^#(k)K0ATw61c)m!K;`kk|h|b%ENVuZ4 zx}`YU+2K(#-}qqA$?u`wJ3w!} z-P5`s_O0?XCm+n)-^)tn-&ji&T+(>gvh+9)dQ1qV>)H zOiRpvd98ET{+QT~NBce1TJzc3BQKyIVEa&ud+}vR&t0^6VZzOsB{*Ko!9QDd@w2Q6 zajvX2@g?P;zXJSHuEums0X79#1=N=~9rFLUMtAL96Xkj~IoV|g_J!8Zd!jVi zmE7}I%iU}2E+gHNS<=2H*;ScfcljXC2k9#C%?Y;R*qTL@^X_XBN)AuE z+q)Obz%~?Y*$Xo5uvLLC_kvtIYH~SOp+t!2j=hkN?ac;x%)|b2 zD9(;4P~U?>bDBOBt;(58rLQX@`ID*8hjz~sICekNqPsrU<2z!$#6C-SG4}M@BMJ6| zj#JQgzlVPO1hxdo>j63JzwDVwC6k5X%}Vej@;;mjc@;wSPJujYUviH2WJBNhpiiek znT?h*+0c#(=oii_piCu{sZbL-HbWWoWjoZ<4t-q-Wu`$HD>udflqrHT*zZ4vGS3R# zyBEs*7|KkCGQ)6OK>ipQBSWG5xZ`iNv_Sfab;+(B>!MtbEwsDFK$}-V-gzzE`(CBp z^~Aa~SK9hiR}_qw$174?X^^)U^ke>)RVl6~)}$HZYjbj%tHtgy%B@zRA1k9=KY{aL z2W&W2{s>1LPlx5W*^%u@#BuXD9ASKw9ESKkuf;czzF8;yWfo`3)amDup>;@&&j~-WMfSJUkTcuQMj^dpO(HxixBl^CVoeN3M(B z_0#j~k1UUl?P!RO>xi{?w|)%Qy-%P|64dV2n!br0H^n7()V*+J$BLN54tK$(g~#C9 zQvrD=oSySG-Qm<%t~)ZtCd}&xw5bAgXltW(wXccV1?T#%KpwO;8_HEhYaPB=t)nv9 z)w&$c~EW&*h6W!zqU>R8v|*$c5bxfZ-o4fmipDpue zz}OoG`NJT;6voYQu-jlD^PK2z9Sb%bwsp5Ig1l_7wFTI9AnOD6ZP4-NniSU?FgCx^ z`g;#AR$V8mRaeEDbk|ku(_B9Q+1AQb*Cvo}2l;zI{t&Re5I?S|-c)C*Yv&pbt{eSb z?R`>QZ)<7Z1M76xYV+Q$ zMmb?@dS6%Va2-tXrc4q^XRi@S72t=xaQ!=~raHf}$Ggtj6I^)BY!PYBnQ^XxT?ojalY4#>Y=^mi_vu-G|e zLaFn0U?=bz3_5&}K8)8|D2vyYGr&$@d_s|TFO+)&;yXa+<51=>=7EepsmQtGnrlJV zBIkP(rhC5t8_+)EnhnP(u%XT~Q~MZVGcV? z#j%j=g>%?B4B9aS#>-F`AB*5P%@}Kg!3~K=3oy;i_#bM+fPTm4+N^VzN-y3r1LnHZVa{6wbKhw&2c8OZ|0(-mPHfEi zg}6rioA4@U;|HAMbN@*5{z!BFx8=P5Y}B-KFh5U*x$m}z*t{Q~1-j+D|9dcZ?}2+a z&gHwIpMM!)-j8~5?vM3e&b*)MKQr&wFvnN+U7RX9lc3%uvR)j2Z4k%jhxJe=_{w+= zxp>RueaP;U?99BU9>V9M=eurS`2I^<7e3V0)%d%Y9%=jl-XnMvF|JGzD~k8t-oJRQ zlGJr6YOD@zE?%3n%X@MCk>ZO>jugW@)eGr5NY_HTR_Wuc)t$~V#aY`8R#naB4wPN0&SdhAKR zs4Dk0H#9bhva-nw7f!yetYp^pMP;Se%`Yk|6V0n@Dl4HkgPNO~%bWedq_1kJsBBnS zQRj#9ptT?$?4o0BRhg+=)MG7Z$#PLu*U->d*0cf)hWv`Ey4HL#yAn(!~LDIhI{kX;~_1=3@T@}q!?l}5DC*cTZ4(W12+ z=ikzTvZelde`AeL%x!3_St?eHhVuiP-V$u6G_I-!nu6BW)R##VaC&)NU4su*u5M_o z4U`N-%6xVH^7@s_gP`8u(hMGIYA(YLr4GT-BbND>i8_Bh9$>5Y`z!sGVtI{NUJa+E zs4^N+R^eZ2M*Q`a0(*i7Ma&OHK8|==5eq_*pCg`D#KOx!p(5~P*E=X81oajW@LD%a zyFyTCsp`u@4VNLMiaX&7|M!C?62*O7L=mCg#AKWzVdtCM9(epW&$oL*$}?Gbuf5H~ z8Bc>Dwh%>%xGs#Gb~$okigHzoSj>?oTEbazJ(mkpl#5(WqZM%(13hHSD58>A76yfi zggIkEs=q9Y!kk1Qp8O}CY9Xh!B0^ATYs0uLOl!mRY=~5-?l7K&XZzkkfh*vD5yJQg z&e??jq7+M)xywVLx-YMPLpn3e z0KYsGYC*_paCx0~IfjQ;b~&ZOG$d5f<#ZD-WfLp6s3BKfo7fzN{xi#7-k|zt*7*Of zrQdKgeZyk^I|}%yv8KQAh53Klt>4(t%ekCbJ%aHtQ!2RPajB5>WfoIJc}P(PE>jaL z_08=LnH+>_{vQ(wxsiq1^3M>_#rBla?J8I95ZjZl-L8ho;gS&^C_?6kyj+;R4HdZz zZ^Jv+zaK7RoPiV6l29ku#lo<3sC&LK~7BfkM zVGCkj@HNXe6`IB5asx)1X3ubr05!!;qkJCK>J-+Dkq73|%lR7cN0o&ma^_ zU}+GnEQ8>{g_NQX?!pG+);0;U)R<_EB(!1~Q?4&lXjs6A=EUJa z9iu2X-Z6q%6o(a2BEjI}3~t89D`T;^j*^L(i=h;}tSrNrTB1A)hrSrc0BMx`d+BFD z%$ko&z+l9fLA_SNjtr(38T#Riml+-fHwQDzjmr9ARuEPx*Y(o4)NDaY3AO`*x=evf zhY&YF+K5y`1g;)JG*hI_6va_!grl%mmW_rtuSS7yQDizr7E`Mz;r}+rr9PiiMAM{wZMj z4s@Q`2qS8-JXTtMSX%yE(DLVlmj5nj`3q=`Ie=aa8uo{vxvvK4@BbUoSJ1``)5y25 z9NY+Shhcy-aI$~oHVEw0kFm~R@{?K^_`&3-XnipG8LA8>PvfN~nEV{umnCFS&!XmF z^8Af(Hk+263r4@f`mzri7hSX9r*Ke*nWy$sz>PEYWs1O(T$8*F5#wb47$Sqt!%k%P z$MGaXH<+g8m};_t8)`;UWIRQtLc~xrmm=3uq!J<``-ixfp&DvYhnqV00ylI%N|8ep zISdg)=c^R?Geu5NX zQlyO{9Td5TBKK3|5sLg0A|m@;+{;jhKD@%T9;midk;~+9P9d?E}Fk>(y zc@+5;MT#ggpCaFZh;i9BiZ((D!Vui4E9vgG(ztV&cej-aju(pK0He4u*!AU*I}p!- zp{N7H>#)gN+-4|SnsMJh|$olV40yo~B zNE=W_-N|mWW;$@AYzLX(J#m*I47YK-whqKQ`att6M^*nD{*DL!cYVq@V||J-8w5Wo zH!l>*nheF&CsPzc+BgB>h*LsQ-DhCJkJKO9w%d0&?vA;q&-dbXCVao|50ZDK+?%G) z?61!npclL0qzBRYuwgigyV5Y_0AC4P3+6+>yO3BCMvnADwi~vGV8ejtZ>Zh`<4*Dt z=0U)7Ow6|Ege0CoA0mf%B!jO)vc`b|HG?${x2BY)Y{b8(9oQ>vZ*m(CM>6^tn^pqR-=b#sR}Gkf?HuA zRgI}KN|r5M+1v#3z@RLw<$*X3Dw`^zftf)&qAL237&WJ#o@p=EqYvsa_St&+9DM+! zVvF@ed%GSzTOX+1yit$dq6@pWd+kP`+FwLWk0PCF4j{)RqP=>dj^1wCYY_qZh{gw#Oks0^lY^{UQcgBU7#A2 zZ8Y>`+qDy_V*(U9NQDw->9Os4x?`LkjaJ&{>Cx)oSUp`UE!NYt!eYIzHoMraJqLQg z(x=spAl27yGy!UkwR`Q_Q)^E{6Sd!}r|sHrK+b8`4nvC)X6w3FPnxOg?b^^{^-36( zz1{T%xGOrwv0opd#T>-ywM%Op&DsVH>T5Y|KZw3j2Pf;pp`z$wy`SB2jXnmd?*~mP z)<>O&z3K?jdPY(7#2Cj(yLRikgZfCj_LTyrZ@?s!vuoSIv=Q1;bW8e1yY>+n+|RDv z4K&rUO7GVOJz>}02dC%QwI89g(zN-f^;m6Gu|5z4&`Sf0b=|(juD!YTpk2#yyrV}S z(qk8b!j@uigrgbF8vrGv?T(-717_;cV7T55`8OAznK@g%G70qP;J9ly>UO*Kp(xg~ zegUp5hSa~{u-LKQ@d%6^?IXysYiY%Lj8+Y`Lf6N@;h>%my1|Bg9AJ(~dj1SO=E=`z z9h$Z2$(c{;1Lo;5MY`KDSx*6tG4>)o`Bo$Ywt$+Oix27Uc}7k$X3lNX2hG)EX6l(| z^g)KWwz7DEo;nwNQVgTmYf!s=i=GN)4VY}7?bTyu>yVc`-8;*x+hNFly>278M4JGs z-R;^maE~^j7+&d2QwKvA+qH>?UmhX9jE3e7DAq?qGiE?^bTJ$ZZ#}Bdn2z51e8Zs) zn>NkzZh#ieo;iDVTl=T&pKkIVn(3W2XU3dgYb_h+%+vdv(bK(Ny}wsao$1xnj97Ym z0Q8@&r_F-%2?rq<^^^6d!Ky!rVu(Sv->1MJI|f#Ro9}@E(sv#Ty=#KIm5tD_4SK?Y znckUj%9sOZx3ck8FL>dL*|^s;;2es!&jAZEiuCAndOxidhXrPM^#aJ82?OK1I5_N% zDf)M@Nc69MHN*RvVXscl;FF5n*A@89XUeMQXjy@Rlq}a8l zVmJ}|7VFtX`jk2PR1L<}7_6pmkv?ROJ`~gGnC^$#vCJ_0T)poMeat*P9a0|qOg($L zUNB#uGQ%w0S8FZ~6wUEMX(%{ufj)T_D2AwOfj$ljrrSa5LVe6EeHe@feau`#T@G9v z#>|nLQBlEMeeyiLV2&}e7ecS#wrK(QPFsx^j@5A9f{oDJ+cxQmaFqeoS;hKr^pkdD zu~xVd&o_OxeWSL2o#PR^_DAfMhu3X{2%bfD$11zF5Bem-t{qiQV}E9B(qp|aM2=&I zVQ7znm!V>CdOBRnyn5nXIFa}1{UCM-Vpr*ja~u=()ODBkMWwVCYENumtM^`PlSgBVk-}G{I2Plk*IRy3MSe&CL zY=Bb=hO9D2?=wqx&eHop+#BXPw^LJMv=eJ!=xFz9c&3==;yct!|^x3Cc0A$+jGvqSjb3_Un`>>Yk-@W6vrAT!g@V?Iy@AViW4ZoHkf zVmUQ2UhDSapBwu7^kSHJ)8NL7*j{;mFgVS!gISM56ed6cV9bLdbqY54)2{%B>8TN3 zgS`@bQ!9vHFnDl05P!?y!SO>Jm1)NO3uC>5X1mun4txpqIwt6 z!kjOreCRkYg7FiswWFXwU(2@>wPrF-=htk>Gno8*#!0?Sa&CXAY)gO8lVxewVi`w!0xz|I zkw39sE3dQ22VOz}TP``uTY0nOXiuJuaJ1p{6 zPU8XV&5|;BcvQx@o&%C|d+7W?e~y(hc=)r7qn>OF?~(C-jN?D%z<~0@&8V^8AvxA- z<$EmhITrbcEb>J*>a=IoEr~0mm zpnp~bzJPP7zm9R4gg-|!1J%1krq?r0Y_er03U+)BNyl#_Ob> zQ;fGu9wm?D=VxR(p7B>?dO!p|nDGlTofm3Z_75%}GVr%C+}MBrVF50>(eN8rC=ob1OBBmQdbevMCUgp(A;42ufmhx*N z@OH-Ar2L%``1cs!Bjtb2xWwWh<7EG{5%|lDll?~-Cp&u>-yUXzHI~ZRa zWy&QnPS45k&*F?gdM+4?U*Q>?aknWZu3?b}+^QE3o7$^N_ z87Kd%vY)9RiN`Qb`iC%1`bRQOdd5fK9>z&O{O=?qkUjAK*o;uZIQhSxaknWhRxzF< z`GyGmHpa=GJ&cq7M;Isl2P5#O7$^NbjFbM8jFbK^Bk;d5PWlHp%!ZRcN&jHRNq=4h zK8|tHk1MG$aQ)Xao@0uM>IfWH^kE=9r2h`a-G-D9_b{F#`JM>;5ynaX3yhQg*BH-{ z`gNGfw)~Fi!rt zH3Gkranj$#IO+d6KD=<%{b{#jleS*C;bx{C;df?lYiz%;ENe2{mqP%{xyu#{q)ud{7%M6e;4Cd3N!qi zagXG`iol;`oaX1p7$^PjGfw)?MBo<~C;J_JOv9*u;SZ~fkia<21FvA5>?vTJ?71ca zpUybhQ^EL^ra9s!#!3Im2z)K$r2qSj)BD)_87KKC87KK?7$^C+7$^A?jFY@-{LNbM zzD)A)2bV^mej)iGj8lJ&WSshICgVBsxn@4&G;gnE{G^=EH!)80_B$A_lg~x>Fiy{J zdn54ujFbL9Fg{q$SO0@?n#aBqfq%&O>rzjY{IpMgULtus$tN;SdWslNmhuZ3C;itmep1S>WSr#JGTtWTZ;!z5VtkvF z-_JPBGahH0^uK1|gUqfq_HQ!omU-_+;2$wg_q%h9)Au7t+^jFUaH7$6O$)E2rPVM@baq?%hTna}0OZFr(PW?L|0w2sc*^|dO*)xuD zvS%UVWY6`Cd!&E-5%@C3sos_dyp8c@slS79>fh~*?~w98W!x?KFBpGa@*@^LL^3@5 zC*wP1-j|HaZWVuHocb$TF6qPmMg7DR8Fx$l0~jAH`C!IJNuI|z&1c3jzC_ATVZ2WA zV#djz@^&cTe~cS{4<*F|EBcU+AP7gP#*jx&^Cen&i1N7>dE;-%Ax(NpARog}^I-6i z5Pwvrudr~`Pv4tqO&$7~%A)8rCcj_m_cBi3rx!B*n3TsKZNh;1hnZ1hzg))2{%589 zEsP(M{8q-FmYn=edVVGOE++q)CpRNzDS~#9}cgp7!8egRU0W=1}F($uV%70|x zsQ;)TEyQOQj{1Kf_d&m_jlKgEobe-<%L{#nBK5m|37<8=RB#WHOT!_ya^BpG*E8<6lbtG2`U_^Nf@KZSs8^wTt`@|97od zKa&6ZGfwk?A&is%M=@@fqzMzcbj&53{8F-4>4bFIpEx>r%*{(9nwPA0^gZA|8K>{=?=Vj5AwFW9zKeXxIIVBE#5k=>aL9ER z)L*ooAdzwUj?$lT`aY2(IklI*BhdO8(ogGqCNn*>uBe!CS|3B}XGjlyC%b{k)A}Ai z%hA;>P$L*E;IF6GIe^gV*s z;gCFie|?eZr|-V6Gfv;L|H3$Z7d*wd;xx~@GmO)Bi;IlY_X)fFzCm`3c&O z|d<)0xXR5ruUClUMhi5QO>%vPIze4IMW1Q|6t0c$% z8YFGRLpzgSAoXmuaI~MkLu|8fwEwqK{$2}5dHPPU+rm+v?5FiYRiiM;76eu6%k<v?gG@tHC$KraT=Z#^&Q zeLV4Z5R0m<*oKpyO3M$Tn}jsXs6Yl zpUQEB+-*phJL}*3WLbEHydR;w)t-O1aH~BZTDa97{5d+`@>Y>s3I8u`X<2ziMWcU} z$Zdw7vJ?1okIVe<(}KB;{5MG0jz1W9{FRBW$c&y%=Vj&tgET_OJn`jWmS#k%i#C>8vVq2>ybYxxXk=1 z!7y$tv*ySO@J9!OzdD% z%S&c2727tM$?5X^piB_&74SP8GCBPZVVQ3s*nAhok$8sV%hrA3z4T31t(enIh+%Pv;R?9p6+*4p4*DoWX@1_)C{wP)qi~ekv-IZ zK49VMZCM!51D zhnpoTUl%ym6-wBFieH0R`|Lle!9J%b( z0Fd$PXP`VQKn9H(dx=Zys#C{Qm*#@-zbg diff --git a/euphony/src/main/obj/local/arm64-v8a/objs/kissff/kiss_fft.o.d b/euphony/src/main/obj/local/arm64-v8a/objs/kissff/kiss_fft.o.d deleted file mode 100644 index 595dd38..0000000 --- a/euphony/src/main/obj/local/arm64-v8a/objs/kissff/kiss_fft.o.d +++ /dev/null @@ -1,8 +0,0 @@ -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/obj/local/arm64-v8a/objs/kissff/kiss_fft.o: \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.c \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/_kiss_fft_guts.h \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/_kiss_fft_guts.h: - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h: diff --git a/euphony/src/main/obj/local/arm64-v8a/objs/kissfft/euphony_lib_receiver_KissFFT.o b/euphony/src/main/obj/local/arm64-v8a/objs/kissfft/euphony_lib_receiver_KissFFT.o deleted file mode 100644 index f4ee0ee38ef5fd49fa980aadd3b3ce8957be58a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78832 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 diff --git a/euphony/src/main/obj/local/arm64-v8a/objs/kissfft/euphony_lib_receiver_KissFFT.o.d b/euphony/src/main/obj/local/arm64-v8a/objs/kissfft/euphony_lib_receiver_KissFFT.o.d deleted file mode 100644 index 1f834f2..0000000 --- a/euphony/src/main/obj/local/arm64-v8a/objs/kissfft/euphony_lib_receiver_KissFFT.o.d +++ /dev/null @@ -1,11 +0,0 @@ -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/obj/local/arm64-v8a/objs/kissfft/euphony_lib_receiver_KissFFT.o: \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/euphony_lib_receiver_KissFFT.cpp \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fftr.h \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/euphony_lib_receiver_KissFFT.h - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fftr.h: - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h: - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/euphony_lib_receiver_KissFFT.h: diff --git a/euphony/src/main/obj/local/arm64-v8a/objs/kissfftr/kiss_fftr.o b/euphony/src/main/obj/local/arm64-v8a/objs/kissfftr/kiss_fftr.o deleted file mode 100644 index 4f856d744794181fb34a5ad752f43fdee7861b8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15976 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 diff --git a/euphony/src/main/obj/local/arm64-v8a/objs/kissfftr/kiss_fftr.o.d b/euphony/src/main/obj/local/arm64-v8a/objs/kissfftr/kiss_fftr.o.d deleted file mode 100644 index 4f770e0..0000000 --- a/euphony/src/main/obj/local/arm64-v8a/objs/kissfftr/kiss_fftr.o.d +++ /dev/null @@ -1,11 +0,0 @@ -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/obj/local/arm64-v8a/objs/kissfftr/kiss_fftr.o: \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fftr.c \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fftr.h \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/_kiss_fft_guts.h - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fftr.h: - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h: - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/_kiss_fft_guts.h: diff --git a/euphony/src/main/obj/local/armeabi-v7a/libkissff.so b/euphony/src/main/obj/local/armeabi-v7a/libkissff.so deleted file mode 100755 index 971aa4737aa05cb3ce1c03b68dbb14e79f6280fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112920 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%0Ad&WM<$Yf3lf6C?E^?ik36L1 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 syT)9762;dTlf4u00k|-y67VGdKtT)=a_ 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

FSXhp6*LOkPl&KH}d@9l0^W*-dNR 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 L&IyQM4!r*d!v5Z1 diff --git a/euphony/src/main/obj/local/armeabi-v7a/libstdc++.a b/euphony/src/main/obj/local/armeabi-v7a/libstdc++.a deleted file mode 100644 index 8b277f0..0000000 --- a/euphony/src/main/obj/local/armeabi-v7a/libstdc++.a +++ /dev/null @@ -1 +0,0 @@ -! diff --git a/euphony/src/main/obj/local/armeabi-v7a/objs/kissff/kiss_fft.o b/euphony/src/main/obj/local/armeabi-v7a/objs/kissff/kiss_fft.o deleted file mode 100644 index 8ea78c141d4ac98e855b09c555873bc2a65f4f34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23540 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 C@GCq3 diff --git a/euphony/src/main/obj/local/armeabi-v7a/objs/kissff/kiss_fft.o.d b/euphony/src/main/obj/local/armeabi-v7a/objs/kissff/kiss_fft.o.d deleted file mode 100644 index bf6d14d..0000000 --- a/euphony/src/main/obj/local/armeabi-v7a/objs/kissff/kiss_fft.o.d +++ /dev/null @@ -1,8 +0,0 @@ -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/obj/local/armeabi-v7a/objs/kissff/kiss_fft.o: \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.c \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/_kiss_fft_guts.h \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/_kiss_fft_guts.h: - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h: diff --git a/euphony/src/main/obj/local/armeabi-v7a/objs/kissfft/euphony_lib_receiver_KissFFT.o b/euphony/src/main/obj/local/armeabi-v7a/objs/kissfft/euphony_lib_receiver_KissFFT.o deleted file mode 100644 index 27573f7101ede57c465db1233742af6d2a85a02e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65812 zcmcJ22Vhl2_Wqgoavz}xK}rBof)uIgR8c~QfEa1I>Ka~>7f1|AOhK?BVsE(ij%96Y zVO`r|ch$A6z3aMwqS#pMWmW#?o0;2A-U~#3|En+Gd~?p6nK@JL+n>R3Hji9&D%7T z=-G@8v`y7d-aO*+Yrc4l^LX^k`?sO|kx8d)9CGV5UqsZZjr(k1ELpX=|8v)Tv7KJu zG^@}0ZL`L&-$qpO0n!)uS^w~(9q!-uRmMUt<3S%}^oeXM9>4zK$A{g&jXLz-7U?r! zTY87AZRvgbZ_DVAu`Q!d*KP2R$@{d;X(gv@j?uxJI_+|+PC0XO{Qp!n+bJHjEq3TW+f?zuZR*fL%m;3Bik)qf*j~}0 z>t9e@o*4I|CyM{d@oTnG@#-(qvQ}%q)}Q^*R@$ERf$LY-pHp;S|7WPbdVM{^^wCQP zoWBMA6#X+jgZm8H{_KbDPh&q_%XPL1sQmog+`{~nBCD=8tD&H9bivqh5{-4u4dsdK^15X?O?7oOO^@{W6reZ`lWTXD{~SS8ym`VmZ5&twN-9p zX-!pGX+xqsQN`{mon6(~IDPtp?DG2h-v�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 diff --git a/euphony/src/main/obj/local/armeabi-v7a/objs/kissfftr/kiss_fftr.o.d b/euphony/src/main/obj/local/armeabi-v7a/objs/kissfftr/kiss_fftr.o.d deleted file mode 100644 index 69b18b3..0000000 --- a/euphony/src/main/obj/local/armeabi-v7a/objs/kissfftr/kiss_fftr.o.d +++ /dev/null @@ -1,11 +0,0 @@ -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/obj/local/armeabi-v7a/objs/kissfftr/kiss_fftr.o: \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fftr.c \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fftr.h \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h \ - /Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/_kiss_fft_guts.h - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fftr.h: - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/kiss_fft.h: - -/Users/jbear/AndroidStudioProjects/Euphony/euphony/src/main/jni/_kiss_fft_guts.h: diff --git a/euphony/src/main/res/drawable-hdpi/ic_launcher.png b/euphony/src/main/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 288b66551d1efd1f13dd06f20a67534d2df57946..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7658 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 diff --git a/euphony/src/main/res/drawable-xhdpi/ic_launcher.png b/euphony/src/main/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index d4fb7cd9d868f1d7d9964f1686dcbc018ef9495a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12516 zcmVAl;2fhX_b_H$!(yr=WHoOzl)fEwqZl|>V1FuJHhd92cP;)+IZb%XWJHo{vS%6?KQsMx!WMLWsBBVA_0ZM@;`i4T>ABg5!-j8KH8bz&}^`6Yx zV|buri=K8jdLyw{MbH0k02-78Fw+6gA^lLF)DI0A{m{fmOL+z@kx!u6?Ey4s--PlJ z1kMW>2pjDnV9LAa8My3(_0_V)T2~_r72LzBBtFHb^6rT*@^C(1@1v}C4aLf(nycHl z=?}E;)ScJ9R`my=a5Y~JLY*E1jp@%|M(<-NI`jZ?&pm{k8xJ7+?tRF-e+!cCUx(Ox z*C77Zbx68+4bqNXgRH)5OhNe-L5bbb$GIa&+9443-Sbpld&yx>!&=>egldKIX<-sS zy>qGOUqJ|~GXFGZma1#b3av9Ux9C5sTB#NZ&|ufk)~$R3C08Fn=A(y@DR>O|%ws4P z5PJlLf_uOhT!QI>li(mY4t9bgU@Q0?Y?CBu^wvZDi3!_oMUC8W z)v2X(m(gHG3vaV;io7ZnAKp>#e%pFD&V!Gjn7Iq7EQ(J> ze+c>PJxGH)kPLUJr2u+AT!C;n4PI~pT;V7vkreoT7Qj4x7Q8>Z0~y@&_tKh9JodbQgAHKsKpOiH z3YdG41-Bs)?m;>{gK}V?8UM>{v`)M|6YfJ1oP%Ka13VFcD;xr6*v~k??g2a4@zff& zKD1yq+%jf1-mzd;TrgppH|QL!USVuMd%5}77=0hYyBq-|FJA%v-%)&vhfuoeKIA>U z0Ws`-HV1{x1I+E{<;n0ADu9J{6n!pQH`;t^gLvO<$VC95a1#9B7YpSooRDl3m(PlzFv>=Rnyss9m4jw`!T*Jql#mAn4AOzrt z0K8$pz#Vo!bAs)UCd1}?*0BDj1+2MZ0?W@E!s3(qF#mU5X7=t$=bAR^BsQ!u`bN~Y zzEk28I|s4$_n_$bZOCTMLnwO#GTYvcmmvYYKNoX-5vHGF>gnYB*D&{A#i$_X zD|kK+F>nU!z)Ac+$G{&Bvp%qY&=Yn)b%E^ei{=C|#uDO@_38H-*171((>4CzD5=g@pCD zpp-cUVc0TGMIqy;h(}>Mo`VoXGzuGv-Ay1CSn_o!1w#ZUb{g#z+8=n|afEgRA9GMJ z9rg`OgI$k3VfzDD*nGzk`mWo->dRKJ{G1tjKZ<`;2j(BvgRXr#(7s(08rG``%9kpi zEbLM=eCtyY@%$}d^VRQy@S2N|{p1Xr%M&YKxVL<>K==X;s{V8zZStz~l5QF!# zz9{Z=OfP|G!Bp&H2;&%@j}8fcm4zTvJEv!6m z1xx=hf!^cJu;TbMn18?=X6@33ncK9Wp--Ky>QxaGb}Ma2ZBv*)=YqEp0`V-eZnNi! zx8pG;?#yYHFE}_DKs%M4kFkG$ngq88cS1jN2ObQbgaeObu#mc7KKF(L0v}8*SYR2< z@k{`O?Co(F8c<$|8?2x~4+hE?ZnVd*Io=sOb%cb=Sr zrvrE3+^zMnc)tg+lhN~Sq4%^kO*?xomSXn9pUet?VFqFE z*%oNtpbhow)S;?Z8HyLGFxef7yOV0UlioT2#QPh7_wj~nPOdxe2b|seIE>kTZyNO7 zw8H+}6#A}BfyNEg(t(uTU# z>QMO`0ieugckmu1*Ko2D3dVi&R;3}b7`Eh$%`fXNyVP#E<=4OE)--nQ6Dz;^!97cu8SHn8juD_DHY0u~-Ih3-Sf(6!eHww)@1n-BhgOV>_9+tP3-U!sEP zM-{LLL-7I?0)R=J!PyW|JdyiWMFEL`rKhb`R-JR)v+lA7v-;v>w)dPd_T`4OH}5@* zcG4F5PDR4H)1fGyHT0gr-u;3NrV=~!>d8X8;y^uo%_X6&zUY8C9#hRZ;_-Iq@wW8; zE;()qi;kMZg2O2OK_e930A}yfgN|(mFmJmn%v|FNCG$0*VyP;6zZ!zjfRY882tb*M zuadbLULfV>$me|WRzv|!KYMr|FFI=B`P<3KkCvaA%q~4?f-RxG&`ud(ku*dzf<-3` zVbO8YtDC{nljan{^0QX3;+ze&mFH|}Y9V4TKST06Ni*o}CvC9Hv4+J|c=Y<;&0zi^ z6PUN(2<8y+ck5xxsRM0Wbf9SiNjnevbI(Tecm9!uvOQJL= zam;t!Z^KeGc@aIw?0;Br$Z|%{5ev5Gs42Vfh%OdJT{4kE&oTWWGk`_M458;J+A(7U zfsjs^qOj&eW`W*qiD#iLIblgXounBede0H!@h^n$+GhxzyA5F0P85F|5q}aiZ`6jy z^_oz(h5*3azf2v)SHt|T2KilTklDs#;wohrf4;;9&(w)y--A?rS~qK>_*zu_dKABACAOZusE9?YYrkB|&7hZ_!=5t=dHhvx%E_KIq=*rO~4Y~}_u~Umqq_cMun}hSS=PRN2D*(TZ3rTgd5LqI{c*Tng?0v)odH%eQNg8?~JXyzD z{wct!FUXlcwCVr<0^wC}*z@rXsQFU(VCGRo?VK;%_ zaRt(rpB2Q9lg@(dX+ctFGD(q;8*sa7i3q^m~;~R+Z>okOEhlr19XT}Pl z_g5@ahO#9j0+cZSD?-*R1xRg@XJgCd`@;)4e*|T7y4)fsD3JO^UdKs6rtNt)WzAK{ zxN;Nn9{j-uKHl}z_Q8f*mY0{DGF;iQOWV4wPw%rg0)hOJBf$UUI^@`21OCp-kaqt% z6bxKqvzgtGT?hNF+hD6{cXHld+o;()%)cfPKwd~nyTQnmYTfjNBI9fE1*U9Lg)XGk zsX^9E9u&=2p)FzEYAw1vK!hb;-LV6WM8Gbg&E73Ea^EYqgFIdcs11R&ZqXjf{n+*N zX(I$JEWDbS_cfq&p*rMsszQ3RGQ?FXK~$j{gl6bKK&&C_5v23T#+^IUda4A6Y+{B@JdylAv!p z9<9aMebEw899GkRA@=5e6V6Y7qtiB)|{(4W_(UzAu2u2uNIFE+|oLr+{^4MP-yNA8L*?}x6ke2KP`(HrC@CT4p{}v?VzX7p{U&E+zU&60qe~00t z{|-No{2YE9@frLu{8RY;=TG6gpFe@`e)wgbARzHK8>#tz%KN{Sk#K0#-971v@veVO~*vbCl z0p5ItZ{sTEcVL=g>(^>Bt(&mGZ_$UAjYN1&+DTy%XH)9s8K0C1%N&BnjC;KRFqO>i zb05{T#=Nt7g&I@6LYb|>!dA0V1FDzf6Iw<>TorO>E3w#7u>SmsryQqA{M5F=!Mb&m z{{01qP3RESd)5@zUbchvS0=;C3;6XJ3%uV779O&OuH6>UvDqA&R+~cgQX?pvuMb6Y z^dNWUB*<*ig7i8~NU7F<#7Z@YE9XH>nG%{JL?g`TQZB@nb0NM;9ulh+Ah}Kf(wY>> zfQ8RV8S=VFx>JVIMc4%`R-to1#ZuucP%xK=k5j}#EXM@rNHTW5W3#)I>L(6 zm=aD+#wpnrmK?Ljsn`;Fez(AgG{cG57`pZtV)vv^+fz~#8rEszEI?L3Rj~-8@bh>$ z11LgTqXGpNS;S$z6UVWZ9%8u!KxnS?{){#`wrsHqTh*&Z#V2P1uV_93=ul+iDhL3{ zHv?eSCjSw&y;iNo^HrF_ZUvTf5e3BNDWZod;u9iX$7Q3-q*$+5iL2($660$YxtkQt z)4E%>Sdpz{RErkVwyoN6fFKXd0`2h^?r z87;zSiN#0?&zEJr;>WV)?xWHPfRJp?@$?p17GdFYSEJ&S1v~_T9TdWASH$iK0YqU0 zxV znjWa3;4@IDDipnPnNWBlSjj?V>a|3C>MXM`JSU@jWI zpxjRKg5+_eCjg{bkLYn%4eSs=w})}gOwGFm^SC5sv89W7Ac8=mK?Fi%wX4;r)vQwc zhub(%tq9YU(h=@+aBKckD^+NH9=M&NLyU_b* zlV}wVV5yjX605O}lqf+c0`QC-$C|s16#~eXIg{EX%VNG~Y0X$h(vK?rnIwH>&QxTv z_Au_zl0A0b;^J>40z?4v5I}wx5nloF<|ttPSHMCh56Sg%EROW7d-S;LhV~;RE9Q6@ zXSHhH&7C91V!zCmEL5WFFcr&q)PrX%SD{9RC9+gN&qIULj1{EtsnIk-2dKrWFn2>B z96i4V`UjrCqbIjv*YPfBUg<;y78Y1l`Zy|l4-sDI`BZ!?ws~`ugx)`tez1#ygc zRjdFZIULp_X6$nSK^fAo0$3^pK!6gE8Ep#aY;qU@5}oGmBS*b<0Ns7T!)tqOnhU#C znY=mjEJC2-ll2tr2q2@COI{EgRl)&`fCr||W96&n`55tAH16hf%8{Bu3wP-v9+WRd z04OSfP=x@VTQ4!;f*FYv;>k6=hA@9~82o;A1w8A2NcVSN94y>_bPaYKodxY{yr8^C zn~E+n()keaNvD%Xyub6M^wTT^5KjOU$qNBQ69D5W0RME!GpUUTV7@Xb#I*Y%fL{Sn zBFDH!Oqla#0Bj2TspdDk8HMvynA|yB79jw!T=f3z4tdf6VFU;PxJQk9Xzn7WSlKnz zFr!KBZuTr$Oj(NfVif5zK-rQZ2)`i!v`T8ig|gB`iu8pxt+0XKUFqoYtKrGBd%&_R zi2gm7UvwYKz}<(JVfXR5FmFRRR4y_?k$BWWi06~rxt+pJjiw)LI|+c~I(dkr9ncE^ zmabz{rX@}KDIk5~)szMfIu4J0Il2e{5=j}Y3T$LC0We+&!0~mrp*urD4eIt z)x;)q&{{jRn zG6o(zz6qxRPev*Dj0Zwa@7Xql@LPQ}Kg0iGpw@3uwJUW;F2tWWS z^&FDENGBw$Ej_{+fTXhYW_cEeHP$U+LYt|F*oZd-zySfMF*&ojZ1yZJz9{1TTzpYn zNNbkEmJTDJ7$YEZEY^Tum1^dCtE4rkU(RTg#fZVy6}zUQ1;R8$Ae18noDuLvDZp== zD&XP6dsIpW1!RUi|F0ed0K+g80zr5A>N%+G42PtKiPZZu+ep27833sPN|=J=gaBN{ zg5&t22p9seP?GTuH%9ulTAnXrAkr&SU*^jc8K>EFB}3eTL>VmfWtb6jH@@X|K;b`4xoEY z!Z)=`?Q;o$>{)Uz0HF7i4oC<9Q&15BFpjnM6xS`EJ57lKXyM@FaUQL;|{y_ZGeZ5?t?%uNIm-Br6Q5n4-O8(t=rdO<+=sn zA7cVWP9vbWE(*?FI01W(ECYVC8VV;z#KV~d0n`WqB$Vkvc%BM+^H_|4alyfHdc%EF zC)`e|&M<=5$U+6yHB9Qw0jTetl1Bh!b;z+I@rn0mU;$2T zl*67{2*5pREMx5|u3Jq2)N5WzYv$nNg#gF^N1~v3Az4e3hx%R%ICp&)_4q--0BqT} z08%Tgz&8P-q#+Xa9NY+Z@7=}%D;V-__SN!B#TUT!8&_b%)@6`d-~sMovXEZx1UvSx zcn)UE-X2J*R-ulPhTczFP$2*%A%I*0Kpf1S#|1_5&BXmuCq7K7lR*Had*e~@ixvnM z??^qt2x6m(lv($1*_ug?!+(1H0QMk&6$F4flhrQAX12>A067F8N4+YgK^EextGBkp-TOC&9!mfW zz~!u7{j^4rlx_-GsCIT4DBmia#0c5la0VLNG0J3a&0qrIj zbH`D-rCnZ%X?2>H)0$-P@dyBmc7E4T8k$cQ^W*A@bE z*rr`Q5L3c~#9CRJez0Z6h!p~eCjfGI;1Vv*m^zO2j6neYX%qWN1Yo|W0E&hNJ2F$y zbuauGkp%=Ar%=!C+izbh0J^={$N#cnxl?jMm-^CVt(iDu z<}ykztI?X1R;O_;bqE0InnY1+PJiCkO3}#t~_1F zE||xKszruy>hhL98MI!2c>3%ioVu_VX7;3kSF{XxNAqCrvI;nV`6yi*qE|n&|2#Lm z3?Qmd5hq^`6(2h`k}@H-T$ut0$yNfFFpOLmapzD1ApKPUMErtz!Xb>z5-IfxSh$s0 zmk>_D8;gMF0I;v;x1sngF9EhWWwdcv>iq~Htx1*^;KUjZ#FWcGNS-8vDTuZ6mb9Y)YBWx# z5CE;DaK8XRma_A@xKP>ip8+5T>4Yv{Jq7vo&d|~m3HKk~q+tHH*?<(`_1nAAITYw~ zOstk00)SCap#rhRx)7YDKmnLIju!$*K>#%}bnB5Uz7-4sAZtowDI>X70SmVZ;~XlT z!ZjK8#cK!9cb5NI;|kZ{!Y<8wC_bAp!_Lfap?bAp5E$1zCHHx67`vluoPD zKAqek!(y!Vi9(da0*JE#Pv;?Fh~J(D8S1AFeTBY3<@Aeg>eev zq;gGuc@qHI5P*Ad!5nP_z+uyyIgr*Y^!{Yb|D?D@Q2-N$0EpXVH(5%j69Dx>0HkFl zb$~_zS<0Rx_m=@&LIBw{PSCs%0X+H(05%W+BuX(=R>=whloJ5z2tWq{`3N9{q#%hY zVJSxHI0-WZK$Zx=TvC7$z%05FiYZqCLUU9EPC+tBJnLWo{#q~SeGXuydtkvFt-B%s zsZAW}{iFcL&yX3400KaaHGc(wGzCCfRuO<)vXRp%2NgXAe*pkPBY*-}@K*t}qhP`a zh{NJb(oak|)`AL6h%TIj0JsEzz|>`&Wk{N##&i*Yb_H5^iTHVQcoaZ-i#*xNWJ9x5 z`<;V0krEQaUhiiQk*nhN>}mM8d4>B_jDVY%e%Le=Kc!I!fD|hN;CTdui?L?zV)pz7 z6REUXtrHjl49*Hd0PQc*5CyQ%;4i13?CSp;fDi;gBY+fNwCFOd0p*$yRiFg{8FC0f zY{1xgtVvLc9!9_f3LtX`Kpq94jL%((E@cn^A(^WEQvzhd-Uxs`==-Q?g_lRcoJrTo z)dHy~eoBLkF#X^YjjNJ@$YLo7%AUZugpXm(+{7klG+Ia^fFnr=Agx)BA%&aF56=P2 zmV@$zhH&!o=KpB*^Z%NHo;`aCD>n)wAf}9ixnGv1pO`WfvQi&ni}WFwuLMrP;uHXh z01*HZ0i^H}@pC)r)ex9^$wHnIMu7Sgw-Ci34rkaWZv;T|N>8`KIoj7S{jgO0dKn<8y)k z-J;ub02l#H|3L%@ThIX*80d#pn-D;*EP6eu13~~K0+OqYAf>_&OM zrRizH6hvCioY_2VKb7e@t6hn%B_+;KX2Y_y?|Ve3dZ9!gzGeVGFFUVU=4xHosdIsd znNrVTiTH^_-XBvg4dI0dAd>(PW6V4xZ1NiIB{S-E5da4P$T6gCrGQ!$q6IqA|KGlI9eUTyf#76)bPh@MdRYn} zh5#tVfvj8~5=tx}EK3Kb1Wp(*m?CEG8)f>vZ<6fc*a{BIZ&hTnJJ9=QtKf51!ROB- zTbT^j4nahg$uXZOJ)<}BrarPBRMzRJ%5R#qEwP#-K(A*KX2`HZ;!6yu#J z!PxnYIcMr1!6|L`lFVzf`aPvykwLFv$g+h90EvR^*}_N&D-;K}i1CnH=?{mFY<~_y zwCDSe5kLX_NdyS{ezGim4Yu!I3vrn)VC?)W*!uknp?T7;q@d^$A%MhEONh!b1E&B^ zzp3LmOY;5z-*`pOuso#;8I8sR1+y##1+y)fybd!qqfwuUFVh-`$v3$cm};2i>d*V} zO?~#z2Zb#*Uq$4phX-YH&f*j}7@nuX1ZQ$t|8!Z_CsB@ZkC0>Rd?#EqaTy(D9wzZ= zV^4@=)m*PbIc@qF0SeCnkd;&lfJDJ;vYagkVFeQ48Z{hz|6zf)e+$6&jD+n;ouQH9;T;Fgzy6C=P8Ip0=cd zODrKK*9srceQM+&VLA+Z`d~ii%fL((_vA{GrG+!y{wV95db6nA^G;f|&H1Q&y}s}) zo!Gz>jgfEqT{0g`OH%yFD_YjaJ67qp=?Rn0_{3=5_KHxwJ|p4_fEuz~N)NXb}~y z>0G>WN(f*9nS^iPbUa84?c>J}VCUZTP|=ux5H-kD3qDB_*p=aQo-0kgpX^shmvU$n zM3*9f5(2;wQxF2kK>*Xa_w?+?X_MDU1}pN>mf=Ne6H;rl{0q><@&mW$on`2C|KD0$x1sqj{2NDvA#b3M@>sZ z$JQabL;$dnnU;)$pWF$3hcTR&h5c9W1N(3KY@t8(2I4iv9kE}Ow)-aJwb%`&*7MlZ z2I2aVC>7C79kR3b5&*iv(;`Q&vLM@>DfM!6@hq%hBDjQ)1-}?=2+dN0gmNwLickkD z@6iZ|eADSFx7LfBUTwcVu1tL(u2PmE%ho9k9C$e$iA)4Q1c6*@l-9(30b+(c`12*O zPE3SIoVur{OOlIfshGsuspvJr%T35tZ{$pj+#fGmekM^-CWn!NlY5~$if_$Ep!a)5 z@Xp!>NLZ7%s1AE;KGGONwW=!Y)AVu&*Qk7*6F3MBkaiV?V3TTu+k(mULV=0UB1>&-P=-3|pQ<|GlgK?gHI9?* z6(u?H?fpbu0-?eG@8Ow~3;w4b9eM0{P0fr>Ajy{gP!pNNYXpZT}>7QTrBApgkrsTQ|rr ziP!HHD**xNT!_j?Pc1Wt^s32_S>s5JUo#oft8EdC^$;9OHlxmp$*8wxGaGE#tVTOF zyJ<4o6v%0Ig4||j$Zc_hycT!7?LjT4$pf+*T!CNf2&pq{Ag;t1BJy>vhUaO_56xBa z56e@NA#bOBsgW4=mO~JJltJ0dC{944?tL@2U%P+LSf4^C~EVC;`ZrKHY)%s=LAFL+z_;2sOSoWvQ9rJ zYM+V#oFKW<67#(d_$8^bUJ;tClRvL%T8yF$c`xug_bKY4{<(g3m`p^L{yiI?5n$r- zGplF+Bk0=x1p4;F!NhqKSWOj&$^H`H5-NpYFo5tJT?D9sLaV^kXhm=i;eeg*1Tc3O2g4~N!Dz}@*3LtnwQy9- z*Ki#5J(<+sxsR|H0jTYcn3zy%bjLk<3^)dlVr_kY1#^!PVB|Og^z24}j`gpgZTTx` zm<|VRvoWA>$1kZoi$0F&LI&nqL3^0-&KgVoX|%@g@I^aWE}OoOO>D zLlC3E)@L+Wc#H;9m(d7gG)4r9XfF;rR-**!dZYB7PvJwy!Gj0C@Ch?oq+#|wqiOyl zqhs?k7&wd=5?qXWzqz{@-W~%MZW64+Gz8!xpZ3lI5FJem!$xH_m|h6Yp8!GG60Bbu zS%4b{?onf5N{~3%Oe5lrK?tM3c*_@)>KokiG;%~lDhO>0-eiZ21{lpkM z4#!kBiUOQGeH_A+07o>300bK_k#!5y9JKL305~Lek;M}syl^6f zl7`p{Df00K5LF@#5ryQc3Tc?0 zhTTuBBshg&B=|~#sk8W>0tiYp?J#p2F0l0am6_~68eGE0f=A2*m>Mqy(-Nh@2aN#r zis7(RBPR_`_U63@FuN~)R9TnhMY5S6S1HBDR*;=4vSA_(Vfi=@WXoVWl0pxb0H+`+ zuyh|M(6f^GR{%bYOt`{1WDE-LJkY4bibCx z2<3n1f+&E5{K@T3A>#zjVPhDtxN+c{DhUC6jHGN?G&u;$l0^v8;Frv0{o-^6o&4Sd z7*@9`@aN)A%e{#;vg8wM+1N@1K%)R5lyKx#%$Az9mxC=v>X>4U*S%Gi33+i2}7`Qyto02L6^ zQ9`l3t#6X-UEdT*L13l~OT-T^An$%xf~aC1y(B#(N1h2tmlyaYC?9eO;Tr1c4f|-= zyZUDghYkH1!^CnOgUBL<{V}CHHlb3JjVo7SqDz#Rm{L_HrbLU0D$rqrGBg>FD7n*9 zrjK_KeU|4x{;a}6o3BDL6>7tCd5;sy44C99LpG(_kWH&IWi#q6*tA*;Hm+2k4bD`) z;+-Is=oK>Zhj&-}mnP!aV(u3KnR2#4nY@)T1qKh}i_F-#B2zZL#DYyIv1MZmteypD z=2#U8&jev7G12&Pn%(UB)8dNu(;iWEuZ6y=Evr>dOS<2FxwTD zqaN;`EIs1=pD=uJeBc;0>htMIQj-2D3R8nJ)N8}C^p;2EnXHP=H*E>aHVB%Ytf}Dd z&--65{~)WHk>v{CCRM1brPUe)X1Cf@6t+9J7Ie6jWi;7(C0Clr#uvW$bGh%2mmrX| zLO!$KoiFjVKcDww3}5d@{{o|L!onmz9ZEU>QLz*iWpXV~e2dms{5HF9Nv+`z{XKwp u_AlqXKi(hjkN3y>U-hg-(tR006L*6lJwv=al~q2+HgKpZw-e0Khy&NmfeN zZ|SfV)qv3O`uSv}Z?K;4m#C5yPpW4YgO-$(lq#GK3RT1%gz!rh!x2z2AhI0c$Qujd zkvIK3MWoz_6}^9<$KNVT-EDk)eQtgXKeuO$`;*s(l^#r=_>VXL)4w^G_r0VFR#=<- z|4e70dw|^=VBG36>BHDe=o14#9E}Q^_G^*IBJ}vZx$4nbDmm6a4vD|eaGU_<!>16_~!nINT zo&WOn<38W##Y|==S3R1h9L+l8f8*OPauX-rq0hD$K$?M&=W&Tu6t^d=uq_ZcBv|WE zXuo5+LSsI^K0D)>U$juOG*|g}YYBdXcLltp2`d8@I-R>qHy7xQIeb3fJfdHiwN;RL zazCIF_?AU|S+WuQysGps!g~ z((g(4R)PY513!%J#u0cInoBiC61-4To!;LYjxq#LCR=QRvxREHn=&Z1Qd4+N~s-{ExI8kEK}3A@kqlDQolh z-3p?6_jaBu8}`ZF-KOThkIkQ1`6U?<`HH?Qe+EdkHi<6{VY)|@{4EIeV_jRR5trZ_o_nr+_!cyebpITmx7(>j=Oz* z5fPF0tyUu%u@|aX;Wa3};iF(k+}U=NBbG+y5sy_CO?^n?C1NppD3}Soj`c4U$5bXP zS4cjURC#H&qf4j;n!XpQ>xWI3DC>uU#$O;UCKk=|6Q_PdAqUx)7T_AOK^?gRyb>WcO!;K{1&l%cjruV~mMejVd&Xj*nkTq>O+6Gz`%k}XP4nUC#!Pqr(ZBOPy6^xv^H!_`^ipDAgQ7= z8+groEnBT$9^~Ws-tMkpE9#H?Ug_O-&6CWQ{pWp!Eh%f-Te7dr#zUIh)`j49{r3EM zDA=aSn=mM0s_%EeLB7$M(4Hn)7CO(-i z^crvEy=ypgAQ4l2Hf~PB`bd6+`v{qvrFsN?AU@>=_yEjIYI_~IkSTuV6MuW_n2h@E zlPPNQ?|eh|)7-cB*P>jU0x=wG+g}BPNLvj&zXg_8Mn%P5*aTNp&67G*qYn+ZJ<)mI zK~9!u<;(VsMi=KEsyBbt{8JX8jR2it?%>(QZX$-PdH#bUPwUZ@R{8T6SS)SQ0Y&W4 z6FO&zAVTIS@4_nF&`LY}bHE0|(tj0n|6Aj~J&eiy{U6&~zqpSMbdtl% z3Ij-go-8}sUQ6(O@&^Ms0?2j*IBk-bRhlDz|sy@A392cUmcUxXKuHR1e8H2U?#owb#VL6|;IU1gR`?ljHets!HEXBSEL3tp-g$8Pbtp#IfnIv5vZLRffV55lQIg^QT1JxHZG z{OO8ed}7ocO_Aj^&8T8COE#R;Vx^DDc4MSgvuyhf>NWh3vD;oOR^3i}Di#T*Q02R; zDMDaaSWruY1GBNL&0tJjX<_JHcR7K$@r7mdVhKDZ?nB+?m1!Nks(J#XDjQU~N?V*6 zTTh;~UdiDf2@!^n6v=n8v17EdwhQ)<6j2aA8ZZwKWA>&yj6@wWL8?m=xNzr2=+thD zVSH@ReSZL%c#mNxW;<3bY|*KIBD7}OJRT%JiJdMH`~0y0RVQJKK(T^Z^Ece-YtCbi zC~l<98!Qo8l1zGBjc1NXbFP3~-A5y8kZvSAgO)q4+r5w%`T-RAQPCvgR1{K*hwLRjR_SJu$|c95h=#6B`Wj&h{}xig5;*(piBDCt`*|KsV}(_8kdFI1t|Mu)g4YJ z*aO0+Adj_TZN-UoUI<|+7hb_5W}b%&6b6O|=0sCl^v)*18>TblUD8hvcyuj}i!0hN z@Obg!`rhCJG5ZU7P^)!t2qURkaCLHM@~wi``L~dn8K;ZB4Pzy>6=24Qm~3=-+Aciw z;Q5GCM(oP~>LS#BIGH~Qu-mmcp*eCHbX4-~Xohyl{=Q5(`&B0{Kac){R2NT!fn)Si z>hh)1Vj!@=R;nE4i5G1b z$yl(8&+S6b;ES7Nj2lf8qcyOaY^>|wb!r<)C)Rm;Ve|PO3>6D0mymE%-b7|zRg0Mu zI(FOsYb|`+MRYCc9VVXq$^2ggPwkOHpsLwt^DQ-F1K(Nly#EY4_b=#+HX6u4)*Yh;`o<-<*XJY?8TsbrhlW|BD@Csv3b;Ki8&5~cJLLB1Bxg#`X#?l_Jat9`1b zcWM7j(q(@B^@j;qU2o~5TLe#<#J#lxXsi89ysjl(lv>V`;{gFI0G8ZC=S zr`um=>SdBw=uVSyyD=encc>h*Wg>C=;$jydj>q($@Mr+ND&OSzt>vxNNcb!9xD_7m zC+nk8S9#QQ51KrIikYmIi}0=Qs(_MFDT8)UIFp&X3lyr$#kgv>R{a)y{-6o2)W%@F zr7}Oz6B71pr*!dgif~L8>Kgp@rFQSDkS-NJdfNQyhu~)-u6H(y}*ugBsaSH1h8o}iS6@*$3bsYYbPfBl^n97Q+*Wq*7eCq=Vdv3eAsBFm;Y~zHHwseOrl0cuwUCckni4mMa zpX`>FypV_QjlWNv-J^TO6?T90r8?dI%P8#YiabMI*LNuZ8oDOON2UYt>_#kn)e}k9 z$+ka^XVIeKs3gBtV3iH-m;Bg*i|}M?Hwzt?Jzj8p(XnN)sNt&RIW$xf4kUc)4Ylv?Q{?SjVt(o+!zfA^uc>NYdBQe|QZX>bcKJtV8`DoAIOLcHAn`2|{ zH`yS&*<-BBh6x(^E(9jm&3|iJE=(yLQ>YOUp|N7|LQpKGV}9~k>7;WD^08X9_#cnL zRW5efN%mAfj2gpVmV-UacXNIsXqlk$I#=e5%LF_CA$FdN`t2Q{I>%;W^UQnH|GjtU zxY#4J=6l?x}x87G$f?X5!>_JnPvKV2**ey-(o!ddu{y+ag%s0 zoNt_W{mU(rxFP0463QFzBiE(%k!h!eD?fh900|;?CLvWx7q^o1jJ4?!si~>S?1I~? zW8upn5m1D~^oF|Cnw3x_ir&8sYw+uV{1yUsx<_;V=(AYzh{T%j>u4FaGM_c{TyV3% zr)+J7gL=3vQ@CC4`oR+(5~!8NhZbl0SS*Vcn8+W!XnntNeVi@tFPYu*?j@?CN;WC3 z-d~!i8CQL|5HAd&`PyD~ahCKj77$3&OywMmw=lazHbCjWF?@KEgDy(5)IElJUHjCA z<1a`NjBc1zk@CyW@jhsvwsxaF7^b410+)mc0b+p7n$Y4uufKev<1|P zQT*1@2WN&4yEA5{R;dF2#I@(D7A<;*rV-{l`!aRR8JCL)_3QWvzVnW}dW&12LMc`? zzDO(9q#0_-@Kt035L3+we;8Nq8m?BDxT{2}UK2sZaQFnRi7Vl9zLZ3P4c{`E3>bas zM=C+zKpsli;Ucw}61u+Ep#0Uj`2&pLobTUtZQjUlJ}5wK5IawWDsr4r7`u2ns=kB5 ze4Q+gh%khM_>f#0PNHpVT)Vy{nbh9MlRC8thFx1Vtx?6`;-)Cppr{<9RvY+W@ZeeD zBfp9>_39d0wkr+1@z=S9X@!V&4b$naOsBl~r6gp6JQzFx(8&P;V?X`U zv^P^6bMxh&YG{k)m_c`Wp&hUL^GNow!6rI z-#gb^S{=tegztE66`fIrgjg_q{bd=$YQy2w_6CL4E!-J`A9v*2ZzVh&iGhknW31H} zJLS~ILmh@E4Y%Q7!w&bH3=6N{Ib^YuowXN!kHoHSpyikKBPnu;o>ok-zd*jf84*-o(dg&BF@+P1xam)#ruAw;Wo|xQSH^9Cx+hSMy)b4y;E9$@93< zYCF@NVweL7rTNKe>l8kVq6n@qaKEt>o=p4Lv-5GwP6Xd2t_#;7cF2u2zQ+KYGP3jz z1nj4_p!yJR6|=seJ!|Ox4&D#ictu4S8%PS7ZbOdr-Z5US^)I|i?6AJrE?K%z@vizg zFp(YDT7b0s@$bD;P_qahv+YkmpQQtE$(4dwLuiS_!)_tC_hvuYFBa8Sp! zBg;G_K@IbSL%0abuRpV#x+@(&1CsId3YTrH23+`~6N#czjH*!2-tXgsW3d#eTDsY7 zVtr{~|IcV0bt?*uWETgq2-dwW($ceh0h2^0G26a1qmKOxVy~!=J$V<_@4~^-KBf@I z$7K|Yx-Z<{4WR;z#Y1TP0o7TgG4iz8^0W6HfioZ% z3nj(W7Qi|S(X5Eh0c;oYUd|*gbZ&2A&|H404J`>GNi`{XRlI=crv8>Q&qT}|i_toB zo0(-J7Jpg!4E&b}$;WceEYz)L4KZ4F8ukwr$v^N9h-47y%gyo6<^Slc&6J-;76l`1 zb+K@mdGTz{DECfYYKd8n*4`v$}6 zH}Tm2bNC;JW21TYkG%No#=MSmD<)0f<;aog2?O`3L<|rya$+Ph4gKDbZ#S!oov<@n z)tG?=A|B0sE>BX< zooflcr=~8Tiq2`l#G^(sXi%b=MP;vPEIFU*ocV&3%kB;RrjDxZ&_G)xO;C)Xx3K=T zXq}+ubV*z{q_DMW=tp(+)4;{KRJ+*%`lSj<$n6UrW#~&XftaST@!*MmlNv{r=%!o! z^{Hah&M5?qEVNu)1SF0kPk}Sdqr;U%fEYOiU$}>>J{cmkAw2j}^|GY6rRn;RXd+Ow z`_z4FM~wxM9HIQFz*YZG=yPfHHwsF1cR#<01KlH`FGDixtR(A;z1{Ux8;cH5&Ob_P zF}ju*`Ui-6yU(wm|J7RkH`W@7I>ww(Lpjrr#e=a{^~kzZq~uF{)$UJ()Y3E>eT zw&Reoi_=q%MT;3MoJx&n`Adm|E8#0cnJg~NVDbjdV1C+Wa$qG4t~>*!Gt0yp(j**@ zC1WTcoOn4eA3WcB4fs&mby_vKjZG}^!UF*_B+6U%4!qwhFN+WVsfI8gz6`Oo?6$H8 z2|PZdaXdV}$p}TR-Sd2GUmJLC=k8MiSgIZk`1>YKDbXj zyrU z|Li-K-|0b=-+~JLyQ}k(Q)yfy0q&mC?^1!9XNIWOq75bE8YrIe+>+nQs70u5FU3(}?$5 zT}U|Gf=hZ_M(x5z!dnaO_5FU6Sp7W_%lllW*h>D>#Yy;XZ=RD~sgbQ>)=+N;|KCr1 z1eu;3eOQz%??$&~B2G29e7~?Sn{Q5^z#gLmQ~Y*lOO_pD8p`Gu7RSdFmkXNO$ky6k za;Ha~C6h!Zf(9MjovQ{j(vC#TV6Llov@9AFx6jAaB`l5Ij}=ELc{j0YtJB!5LNwCn z>7q`L7sH!~B%S|kO+}VA8o&Gvvt8|D*AZvC77a0L{Bx1q^@yQW_M05%OJa-GvHh?b z@wi!OC{ttKy2-tV%zy)T42SJMknqMu#NvG#h_O>8VyMXnl&AKKADtRlTr4eQhnu`2HtUO-nCBi zQRPA6J}ylX6#2#foyE=h?NWXI-6g92X4$O7zVnWHGUj77_{pMwCAa(fWmocHi-_M`GA~T8bnGDnvuW+aDw4#jE(kSwR%QmtTa{@%KYHbYF4W;=7R{#7cN#EMn zrGwpfP2Fb{_E#OgpXt%B+OWn2$S6Kpc|$oCi`Y393qE~x7?!urmARBgishV%isV%V zHj1RwFQxeSVEiy=Ta;4gE+dy!Q?yDhWl5_}!k3((nsK|3dOl&6nx@Ep1}|h$Z^9Vd zu2gNAAn9lz=WyIyD2#rBpF+m89?!alw?}&gkXCs~TdIkwlOY&sy(_I5PYWL5Lv4q! z&xtN%U7gmaCVEc);`2zSB=h)QWg=f30(kI3o#z=h}*O zdaqC}!;aTmT+iKR)cT7~;lbzwpUu7tV#}{axpm?w7Qzk1s$ddDQ|aqUeQc&$emSm%iyi0DpV37O9{^zD`Uy`T84){^p(9x`D9V=81i-S4?cPriWDZ7RZzYeiIXrokt6 zoTqV zq#0G8emJjS|@n9H%tvbZF+@)rS1| zyCZD#FPUHu+?fDG z7UPNkCSf<2am-6(MK;`%ZTB_Fp!`_cluAzz>E-7^n?mQt^_y%Xno%pEPuLlR{C}jS zOx;IJ6wEtt8_gw8kmM=Bt-fij#)jzK z_~p-gDhxRlEP2~;vMA*}oKAvzQdJw7zNb7-XqwpCS#vg*3-mS_-?5knAaUaS1>{)lNq;P_?4l|i)u-9c%nZ(W?7f%L20-3EMkV0`&g!88pp>Zc zHP^t|)3-7otKMyWcR&76*3ZkXo=7-PBV@au{K5LIB86wuEoxFYQU77lbqc5M;rGa5 zP9`Up^fuw|X&KcHWzu{zU5edbU2Ya6Yg*UqLdH$TnH5BB>SLCW7#+D1i@*3Sy66}* ztYT_slNWEy^eM{`1#L!mZ$ptu|8e4cUb&C>a&cpB`NFwt9E`MN6N-%WSu&z>^(~@2 zidk(7gS7Y_S^E4<_$jOOC2Lgm%X;z#Dl#Ven!+hn-;eWmLCoX9W4$zZ6UnD-G#fWT zV5H~}Opk5}DL=_DDdoa0^VxCSopFnwbN>(}56I=@A;7%SP>{)RO~=HsBR9pv+uDLt z&V^qu@MK?rDrO4mkJ>P9ab;l8CTfJ)xj96Usw>)ty&gEI{MB~5Rwav|t)5CeV1Q=F z+CEyAYf3m(_gi1_OFE&ekj>zQ)}p1Ckydo5N3b+Vb97ZnSs<`aj@Xsi3M1gQe~+hE6tldUVjN0RcK%{OpJm_3(H}qKI{K!sHGRGKb&tP?b*a(Q)=X0JX63>>n(=zL=Az!@ zl5>mdVLcMywUE9)g;1dAM8tHI0FZOmg&=(onBX$9a!`Q>w@pAO>CqVyRv`X#TmPSv zvxBwn1u{u^`Zx~vsw?0p9Uwg%rEBI*gt<{9$gdv@`K4roTT~4V7X&dY2l1%PK#Qc2 zv>o{E7YjbjlGrn(Mbp`H?6|H6$dojUm(PbqdT>sq+H`qjkQVyO)EGApsL>>htGHFl z8!Uht`P{z@S(8N?{!~xqT(E85ehgH`m0+Y&*ig zAFnyysKb2%tMrdNlFb&JhHQi!lY5oxjul7wv zo-W0h^Ri3COgy@E7d!OdRaVd&{CzR`@3rYOjgi!P8AebOywO8pCVclQK)2>1?@s>w zGw=5xf@>w1A0$Ab{u?vW>eI8^Wv{C6;|K<}#8o5`)wKBwfv?DZ%SUzgmagi+YOnH; z{$y)_v)DaO9gP#m3XZ(~?Vy9CVFRNIQ%~{)UNX}1zL-|590eAwE9aZZ052+zW+RGD zT8s)KyV_avF%YATO_#Zka_q9R%D=E8;%c_SF=c&hj6q!qg16FuDLMG%FW~0JVE=x` za2Nd^xUcjhFmOiRsGMzZbfSI zK@c%A_QpLLdD9PZndDCIct9B+0KJ&alB@=_(?iN<2hl5WG9KidH~$35+J?+(q8PtR z=rpL&R8b(YtXKj5mawY@h=W<`wWmi=TymT+7Y3svAy6Y1Nr4>(6~0`>@l` zM~S!-tDKACsXZ>H|IkSC>C}@e??2(q7tzh9{;s@wD~qdKo^luLTJ&I)03iea<)r%$ z_kzX-s}5M(YHCS?=@KP| z0@j6c6~Xn5s<${I2NeJXcw0FViaJs7rdj#iLMFGS(I9(MrSD!qMsdi4$?n zZMa1~DCHLW>)V@1O-hDnF9zMCABfjvrT)3N7c-Qkk6I_MK*i_0+wyS$B}{JjWgE?k zLal3)6;<7+l~`}XI*2xoO@Ovdm%uWCpO)8*po&pVHEbyH?V-7kC1M$iuZ*S?4<59o zR~#{wQPabrMJE|EXg~MrlLSF>H$HiapO=|`W(W|ZWN^v=0hnc46!xIP_>9S?y5 zN+LdLviZ6lzWqM04HpsVfsH^UT5yY7o_>x4B6%K}poaYUEo2Lu^EfccWydtLe(kGG zqH@T2+cCcdRJc`1U=^#XG|_AB{!k>zIlSapQR|9DvFA?f9i{%BP4NTa_?A~Hi(c8+ z8+DJFb8#$8%Ic$5a&p3LH0_=+fgRZGT*!@{qK;1zB$mtG+^cg=r(99QOlqc>$6X_f zVTs-d^(YdIv~kdZ+w`~ra+A|95CF#V-f?xEW+IbFfB+Px2AL2Et`y@B{w9>(52m#}8yYcQexvbKV(5{ArJKxXtl(zsENCa?Q;$y7YS4f-Jz-;&$Mn;yGsbO`h5H?3!mTo5&p{x& znwKK>d{>c(GyyfQC9RVG?(z%rWI_W}ESaCQu>|mX2O$eGVUX7~gO7{&qZI{$wGcv! z#q#gno|#dDiS9muU|I>YjL&p>Zs*iFf4;$pTQLws4AE zb0(H84O4YRlT0yUWb60Kn;U^*2J4v_(juu$JMALocDr<{nM1Fb$3~{x2LY&KBb$km zS;J&bDMnp}bQm5;gTau6sDqX{wL^xD9S8+!7*V~2x$S?Jnf6qB+Wd)1UfY$Kn*>a5TRV>S3V|BQVgPnQRAT>5j<|6gW=lj z&OT)vvnQ%1)ykDZOc=q06}uiEo6p}?%4)}6zLC2}Q(P`CkUJ0}*a^Y($v1 zqUkypLJqL|yE-v4-FEr{7J}Ju$c;cnexH%bSRDEHtPnMi-YYftV#OkAzxN+8#b#b+ zJpqH2IN5Fdw573tk~pFqVq_L*CaPW5BbEv^BG;V_Ev#Zl8(#PJpcZVv3Uhpeq!uD{ z_lflcxV_Dpy}r%^Yn(^Q7(-%!cXxIp=LRcPYyjD`Y(EoM@znD~`O9L49BxW-hemF} zPhKGbqR9)&(5?z+mP#P^s3S`*satN8jk2CvWD+M2C&x>%b2`SZqf>v|CxpSOSg0@8 zQ?*?qlQL%rdvY;b@GT{DS$2&Qaup3bObeOw#~E8$MmIK^E(%Iu-~Pr^`VoM}@~(2z z^r|_0e?kk3%el3%1tPXg$Mz2>CkAuxCEpF@ht|Q6x;zAES=7=&{Nl=C-e=kAplzzp zIk+iby+j?pjh%x!^K5a+k85DKOa%wBUbvp)CxB?pf{1|GbKrmq_ z8W%C9NsqMu_hHS{W{&!Tdq)dYoQ@Jgd_j`P})UNd>NW@GW1&tnMWBq#36ScXrFCuBU_Q`-t*Qp>eS&1Yq8>bgtCnBO^5SQj- zn67sSNJAVIN6oD99T6M;qXwD;&Z`sNkmK^35rDn`@Q}%+f#>nmMH=!tr;At(XkSq zokY-Lq{Nc$q6-9V&M5s21F+)3P1PWz1{VKB{05qyuGW~d>0o~$nvJ{_`{lG{_D+pi zx>3Th+z=Rra7sY5#1&hsGoGfauS=jgDsCpLYnNg7yG_>so6GiKMv! zTBtp#K6>h@H`)bu=H8nI$yRwi`>j;n0oS_X|I#x4D*IKTELL4kuJ7w~u!sdM;m^cm z9na>!D1Dvjk`C8_e$9Y}v*7|2kw7}Q2t^>mg1U)%ztFEAQzb8x|FM5SF87v-;?%4&wvBu2+R5SwleXc&8rxi-Osqq`zw6Q7juaW@!cb4V?h4UW)zxR zo-P@iP&C-t>o&iONVr2ZOY!}1;g5gWIVe$aiqLf(T1CIB{?KV4)VN0nr7$701PYu< z8+oxLNH??@$F4Vn`vBukjQr(+ zqEfR-AEi(!+a*a|>YK(Dc}%+;;s?SmtcmhQ-74}(pxCbFRe;S*1eSLO1x>Ci^-cWU z=$s19b-McUn#h1GK4@eNGVt>^$ax>o2~&y(fZNQWD;|dvtUzFO;e8Kl*=JCr0wE@I zygZj;Z<%)R>dOE?w?-{c743Cv5+HsJGD>9lJ?(4i#zfq=zDl1yff~dB{z7s3Q3%v7;LxfCx2g63Sc6zA`vWf)&Ib# z`%x~Zw6aO9?=l_P*3~Ke+IC*p4x9|0mBW*yaQ&hFkOmmMZBs}^nb{977JYG zm?oAl;UYt&I%rdks;&r-sK${Z?NPc&D!cQMDPi^6go}H4U}$p{Jr!X-JzuNDN3yc~ zqip_1R^)Q^1V+|0ziMj0w>>n?ARIW#wGF%rMfc?VQ{IohG$gxRa+%J~x0Yu-%nZlOF%=2NX7r&z7ll5=(yW0{@v-5I}2L z?jNhDIcpd}U8PT5$CEkZ^bz})I$=lhk?meofwgy(%G*$iyh7wS)TF(QY|`C;2Yhw2 z70fCh1!>c6mRNS3$Z|`6u~hPv)6-FzP8b1=A`t`_j|)(igQpQK+HXX3^!j$Z_Y8bP zp%u1!k?X#{_^cF9MTqzdnNW5%a>t`rdys56DT!gtKrPP$3;`qek*&mVq4TDgnn%>_ ztQc|3DL@wQT#+c|sF;{TiFIW^0sbV1?acnP4R>Ggz@kRml*8{XZZVp|SY?ino5Nnz z%(^VJk$IT+Z0l1wOlnrLCb2`?f{qusdmbO;zse6aiFACg8s6ydFI(?Dh=KNHyh`-y z_Bn+m6`|Kqww{4URm-kltF6)f<1Ssj#-bM8w0PEY6kBUGYBJp8)^Le1wiNjvpue=R zMTeZWnSpqTgD}7wdKkUaUne>~ddZ7VWi0^l37yTbsjP!qYnDQeJP)RVw=`{$%%$S~ ztcG-x=6I^QP)5=NRvPnvtDz8J>{|H(1+b0@BNFo-5RIf@f8w0pBDjXF?ZU+LbWoPXV*nV3ub2CtG0rA{7*S&R zXXn;(rT=0mL-DWtY+?g4@7r_0*=M?$f2Scau!dTD@OBM3R&V2l`AyrE+u6Cj-{Ur( zcyr>+zuM6GlSgvDl1%b@w(qx8%fCWVm(-~v`@CmyKZ+o2qJNOm?fl?`dqR&~p3esE znZhq9;pU7T5P;Wm5Pa>@U9`uGcmt<=1OHu&hmgfa+?B(~PLLY%jFITgPS8@ww}?JN zu8*IBolM%vW~u=AT%vU?jF-ROi`phBzzs&mK`;(rY)888C@LreCODDlgG887Jz|L- z`0tS2OmgUbibz3ra+->QP;d4wJuDvFQ2_Xs28_Lon!tH4SSdXzuTs7wf~=e;*6DI@ zyDlb!Jmn!{vDWzs{;$c_aE$wf7|zfTejqse=|7Po`)&_Nd>&ZdXARyoTb81?f%0a! zUs}0Q!r3gg@~61@+20b3Q2{*q^RwoG`KTO_XY61-vqUVa;&EN(mL^3i%dTgQVlQ$V zT6YW3Na|j#g^-dfcn}cDRDh)T32R*Ey^So+$qHch2y$0ylb!t3B_N2;v4}^hcg+l- z6)R=~LiNCkdMJp_uT?JydC?o%B|om`1*{Z&t3M`BEVioK(;aWp!oBZ+0Uz^^%CDJYtu{k6uZ zVUs1POj|`oh7@vu6+>os2W1+Bk?UmOm(=~4Y^iL~wDu|Gn$hbK`e;OC^B6^dqjOpK zoHk2=Z&P??U#w<1^+$@ANu>X(N*lX03Eh+^%xf#Gww@MzeTkFT)5^M_08^!VM*Eef~GOB5F891L34T~2&?nCgxn ziwRS>RhiNHaqGv^^Y>+h(8b+7_m-`_dS=MFzulV#v?&Y_lzR;QH~l3o%Or^HEo0aB zdeNe%L6ZuL(8)W>Ij8Zc1rR~35pRB>|IXCB24H9lfUbCqrs-GQZ`N9@haN1~ZAJjs zb`UV+C8x*G)c;I94K}!yw&JV219u=+l(n z<8pKX01q?*^a(mJ3l31Zn~+#AHg}!*$Iz0TX+mi&{;yJhh$aWJ;wM^^{L=3MSVm*E z=YaP*|CJ;Fu%hBQtKAOI(N{mlewiiSoBthfLD)pY6_FT^woX(9u(Az7n=B_E{pf%j zjd5CfAW)dcFh$rK{2x8mcUjCDv-U_UyOd!E-p;@zWP@M9n0&>V=2Cw#7p7Z4EiJ19 z9Rp-HW7+>9+*P<_caq?-z1=Kg)*vlFESJ?b1|XLnHiw&15K*COT5C2BQZew$}%=Qm0v!1LbL>b)PGxHhE-oME0Xo={O z&$R`sPIl@k_`ibA&hEfTO3{dj2SBa+3t_1oR=9vDamQa{=hO2m&%Iz|Tx3@{;8H#k z9!8>t%d;kz_t~l2Flh-$XG@0^IG~K4lAgVc?D=~8N-@>=CD)x0;R{A=ldEOv8uf4! zY;oKcsxHaS6n#)BzIJCcSp2&ez9uH#=dv%PPJCS$^rxzNj#kaWxmfbQKSXw!YyA6+ zY$=g|4Bb)I6v6^!0i!4gK#og0tl7Vk5UL(_UsLDxT#&OX$w2(4I`@ZcjS0G@04lDU z)$i0D?OZ_>B59Ipjkz{Q%Ih~M$Q;E1)?c8OvMqs z`5FBV04r}d?KG#*I)RG zHeqmo%DXmJ3>(o*mbLz0F@R?)2qmAVmb~Oy0>(-Rg`>F(b%mj0T87Q9r4^?4D#l)d8a3lS;eInWL*=(hwp*0;XY? zcuWQkukP+q;ABO=gH%|9xl0n4pTZuuU^Nou)#1yz zbB!*76QU`jlHxB_->rkLnTgJzSD^)G5d*=#@B;qoz@glFa?*gjbs@WM1014&_t!mi zRZ9!SE^tNi?I2BGfh^N;pzMxi+G|Z7N(HN%mRRn{Azh7M>$t0WD5_+|XHp0PeKd;S z7<)>X*;3>-$o-oVTC%5zJ;h`o2jmThf+j4f3wpy}XaN?CS++1>Ub;mRfK>;c{~Ha~ zJ^S>};CY=);A@agmnORAlr~lc2yb@qozjr+7!1RzUN@o&P=-?7IZTLGR6c@^^ zQYSee+aVgRQp5?8tjax+k#)jwJ0Qipfvozn{|~!Ra(7)Lom;{);dK*dnY`gH`%|6( z|3+I0PJ_o5NLN4x9XBkx;}!mTq}5^x0m{q(*3_qKNXjx<1T_FF6(n0qp~R;JUmZ8^1aC2VWNj_OpLrfF&KQ6_LbZ!Dw>^vV2on01ie$e zGEC39Obh)XO|%N(cTcD;lm|TdItXbBYy=}jlkrvZcQKSDhPFtzYg5QJ${?(}B{5PC zTZoZJYMP|pcZ=l^KBFDle~VI}`r(((ahIv-k-V{Zfn9E#4Wzxeg0gr*WQlOyW^PlE z2U0+Uc{V5^abhr2NXo36ccEg!bAgr&yERd4B5L|~tSRA<6y(_Uoz36)0xgC-n6EO> zl)vrKJqdw8dPpk)V&xyYq=_eks+D_v_lFtsZ(N|1a_vl|481~LOT^$To9=F7O7}$u zEx!udV|ujW@(yMR;6tw1D>0kAZoG)U3`ykDww?@9`6;J}4K0=~$=9elK-1i7$Hbiv z`t5f`no*O3O0W}@h^~u~T)4V}GC?|6qc7kg=FpC#hZV=ou7xCB7o!EI!=olgeg&*A z>}RR|^0=v1VTj!D|9~tRNCP8ATBAZkMlJy}r$f1CGQArZzktdwWbgqw(ZP{dWYK~K z`^ZO*i6)$_(ZkFxZQ#ZH!~&u#snJ8>?OqmR zFXX^T0U@(AgMmhtr_?Zq+ac}rQXUKYh{I<5*+)^rSfpJLNH-eL92o)*#6?_(5+V8_ z6Kr6`65uxarI#1d2R^iF5Iw;DGpaP47#FT;ZWE5LibmEW;y+l(Q~y>6Bi>#6I$po> zLXEU9Mcz`INa(NP&bL5-?mU)eK0vccLR6emJmmtwmu`wR(Z-^SC`3|3-q0%ACm2Q| zWW`9)4cit>$?F~6e}i=ffK)ZBNb4fMqy`wr~$zC zIRX6gnF&_mwPTd-lDh-&IGzO{KdQmK@Ss-UfkY&h0#J`|0G6=5<+oBOU^{z&6&E#y z>G|pZ0xclY-#kF|ocd6JYNP>09Og zM*TkORR9T>jJ5 zSQVPWcn_3VsX@2xNCB$Tr9l89*PkX6kFgXWa%n#!iN_r67ZwiRsOsqS`?wbalrpGr zt;^|@8UY{`hEpG94xl9*K#>HXd;};*5rQ&?gP&+H1<1vBz$RPQK{n*)B^BG1#ue$^ zh$>WK;}M{E9w6FKT&mU+pgII-DFU>f1E_ml>R$vX^j`;vJeEi8_oxB|Bxy@yaBHiJfL}00oOsf!+g%D$p`*syqTf!rLa0Q4Ru7 zRDmukkg<4rm|lmi%RogVqyD{9fzF+4_M8$i*j> zV9o*rD4hccUu~@OOu3B$K*`0UO5zG-04gJ+AgVwblLA29YtB63Am68FDKuorh?XpP5U?MH%8tjM^+g+L-YBE)gjkY# z3e?1r1uBxLMXF3hfeIUuugoGiEP}&^W-F3Qy0GLTN?5KkZgUivaoz(jJ9x;a+Bgm; zGTU$AGSA}F3ft>(#X4+!srD}dBu!;x8+9#NA{Yi<@%{119|F;7E>KxNANC#I0JrX3 zL6F||*pBl)NZkOnL22FJ2B_!YJwrDXzIxpWm#>|H%{y1Z!jfp16=?^frVaqRso%il zV0p6Nol~E@p8^!Y0~C{Mj{rHbA<4Gj9;79WB(o*KH zi}*pWNnbxrV-~+nE}E(nlCHZqC{4XHI75}0yFlk%NS3-JI76A4ouLLRBPAUW2j@CQ2cOIVlSxOMp;^u(hk%ePwEYsm}m-0T&p^}Tnj+SJfYlk=h_@R2O2^_0x7m@JMjbb1$-Y56)d)$($X$TY_{N{5@V`X6}faQfi9@BvQ`S1p=8 zSM9NXtSQV+u!UK1BVlHgF?i0^1CJ0rm>Q@DuD;qZZicGFdZK*c$nigDsHpV&z3`5F zWlh0Fg{yq7<$&+?LFxQ_iGUPCA`b;(Q=B->xw@%^=)cG+5P5^wsA=mgx7taF7Rz z#DC!#s-o#0rZmkXRCU?(F#Xo)VMbRx=NR3c7GiLJYJlG5N&eagT)fq59H$Krv2p1? z)WY>IpVGH<+%2L2l{N=w7dHBw&#jw80eas=3k^l8Kr&t^xrS3IZt_uu_MF0U6(KNv zD0syV2J{dxH^m+nRm8xV^R4jwMTd0sf)2(bNaruNascsF*rSe{d{rfu8fDVkIdEh< zq~!R6Z{$dr8mt0tp+oR@koU?^B$ucom+FNe4D!AiK^esJDf0%J3sSz52+jUEfE>Vm zwtlCbtK1Gvi-BL_Q}SUy{!Mqu9QvHx{vUmF(@@2M>{byGsF}vgVNag^e>$=haW{0T5Tp^J#2p&uUD%je04kLfr#w z+&+9Vi{q0pki4w|_|Maah2?W#{r073cn?GSlLu6RNCQ#|5bXmkLj}6U51dKh?!6mu zsBI_Ityu&~3%tQCNDU^<9t3W42U3$9lBGt02+dZ(DpQ$4LxQ~V3h_E&0z3jh3uFPZ zkpdK&WDjnEy3g$<%571xQ2A8GyL`X_A${x04}^VQBzT3A-ltc3o_`b~&LGj-4-MS< zeQxL8@4L}HU-h?e?f0qd1&(C@=-pBU3P~tz^gT`iDl?`4#q$8kcBFfFp&DTyzRWI= zay4#@q=XE!aTm2BisYTpAIAHC1O73lu&^{7S`KZ5r_b8y#=vuzj?gNM%mWdC9z4EI zFW2%IAzQbl1`;y8V9G2luyg+!+{4ivrzk;a7U}1x(RD2$NPlMz1*Kbch=C3hQkVcx z7J8uE5fDWIa-b$UIB_(ldEF;A69=pvXfg1QvH%Is8Bro`S4>ZJq3`^NR^WfZ^ZZ>; zB;)*h93)+F??T)?mm;e`C98sc7O$MSGp}w60Z2k-TS!_GAQE9JOZpjzgv-dtZY3h? zXtMK2amA{1b06_YUi13HguuUolUIL;M=%by?!ed#6=WhY;uF}_S`DWz?1!zpRzpDK zSQt6s&tUKQS5zE%2u>#+hFc6JUa)kiPo;pFBWTdZi}tgP&_?ajzWC>=o1oY2?&+m1ZrSB2FkV zW#X2oQ5B&b>0eMHF1U+12msNj;DQ5*+m}XwB&m(;j-9hW31+1ZgDIhyh9c4Q$Js!L z$Q**R2Z3m@BLv0U!X#fEaP%6$nPU;@pmQmR?kFwbrwbQxGtOMde7!)e2UxNY5d$5P zNdPj1s2p2}%yNLp3>UF?xH;?Ur?JD<^?Psng5$O#{~v{jSEKjx_Aj;sNaU7#f0b44 z8Io2u`Ru&KmTc4_4TdxoV~aT@5kTqz5f74Rsaj8fD&JS5ZjfkNDOcjGvPXq=E3qK%F_PCP^C6Md=L<_qv7IXW) zWFZn1i2z7FQl;mCxD#xU-|dL0w|R0{1U(|vLu{NGlgf5t;0X!A>^ zjT9}OToRjS*EKgsON>uEgC~_G6OCj95|YIKQ3ZvXz{oF`(G^9px;lvhg=7mUPyC+I)bBy z>h}M&cjmEeUD+LWGg;cC$t1CCYv1>Mr#5a9MeQ3UN+Kz0BdLudMTv_fQVU5@q$G=y zMJ<+O*>N0al6VI2v>6l~AV5&Wbvx}plXOMX0!0TiE;?J|WEv;pd#C3%qv?R@{> zOfjvV&ZYV+=|Aq8NS^4NNRN<8)1Evr#qVFa+?z~I(F+ai4Lo6Al?6>KiH5a~f*V8g z8UHdcmqZOalIT%)25nf*XQsf`O##@p`6w>4sU;ae>oyt80#rUD+r=Z{z}xl$n%TP# z!OSiJq2LGv4*rY-vwKDO^J08mjPFs5_a*qVQdAkt?GtfPJbaDX<>BvnVB6v2vv~** z52k!vu#i$!Zz)Wyl%U5chEW%X9&r{@!}daIXr4n4%;ZsOYt~Qur!sH#Thf-4hV&Ha zI8*q=hOYnb4DNUp)$Nm>8FQ6Mwf6iy&1~8~sBDQJXy%eX9VRt4o+4%ycV@F1X19o{ zU}jr{APJA4;(>JwA=@Hgl`tR>AdW=#Pm>& zE);-qsRFFtMzC+T!@@-wI4`z?W2Xh?cN!S5Y?~s4s2nB0!6jaTe*{=SGZ%$0d$Al< zfgp%rem?+*eZLx2gRsi`=QY8MuL-Od z8ewX!0VY@L$t(-Z=c{3SNeKGIQVe5-cSoFge~+KtX;>-ACe;k=l>JN1iI{by$L)6M zlbhFOV)XM>-%^?pvw zBLl~FC)l^z!M@!J^V=;%w0%cHIW9{4j@?#@$cb9mZ$q_Hu1g)%;z1|9cpwM&fdbr@ zyHHB-T<(FTE4`>bMxHDE0bM5Q#lP(a*S;JayKOML-AGSe5YvVw9>P=nJ;PG*`LPvF z>$oR3zSl1J{lKEC{juY3-C6q*a-!po%D`71dqKv9rtAI2qK|5o!BF25LN}|!K{^rx zUE@iBIR$7P95A@KI7Kdp>2(Y%8}%@Uo?(7lf}TmrcqrFyCrSnjyE59fr=Z=Jx=`J; z=b(pPzS2vtywp#xTvgHMUsluJYXh+U>QF%67=jDm7)FuT1F&{o1FNs7VEJk{EFQ?f zzT1i+wIQ(Ms-qN!W=m=HWErK@mx4l{Ijx?`7L2bIhYi^Co+j`xi>kopJYOw8p=5VcXdj6K`B)P4n9~rbY`~lYbPHt& zl>oz?2*XM(Ok;SN*=YX6zSZ_K$4sjD6o&#Y^2vl~2Ay|>n*dGNgniyysibqeC0o|qyT5Zeu@tC{#uZ*i@Z<*J4 ze`)awzdyNF@psd{%D*MDZC3q_%~$>Yq_^r9dRG~(b(Ek-;UJWG&|}JiE<*;$MpL0} zFbNv_VxdM61>%kfC~pk~ep4`%)*;-I1gcS9NK3l7P*xjRPmcB^-xHE@3wC550YKk* zZ|4QfTV9@6;@#~sMnT7DI4v0rg{J;cgee57WFa7G3qg2-nL*?>gy6u1LTOD16jud9 zq39fNYhs{C6zVS&p8IxAVOSP<-@JiSPozmtJxQ+8qpR<_{W-lwB2;Mj@8t=f_2=;a zkj@o6OXmrngTl%eptw2+fjtY{`d|<=he5d{0-qfLmC{J4Y)6pVLg_k1GTqu+NVWBp zeZr{<6Xpq?4PqAwA0a^RzWd#07WXB)mNouQR5P*AZH}Vl41 zT>dUc{DQx*@_CvgevvM&IgMbRVE`3MLh%{lAa0L@s*WgpZWL5^l4~T0q@i^EF+ja# z_ll~61uQ_1*xrBt{ii%vS~sme!M_hV<3T+eL-$Qb6Oce|h{R+%5}GxU2vP*pDI=KT zb`n-Z(ue>^O`#xajRSr|gukRF{AMmUCiCd6KTBkf>uhe{zWoQ2_8wlpuHxO2niu`W z)i2T*B54ATum$t4worsLoPniA9tE|E=+6`gXQ6b1G6h=u7=S(|0FkZcSb!ewtn7*9 zt8E*zJHj80x)We%A&wrf#{%(2y;Ctj4r}Wii-xw5XlPMKK|>E2+{h!35RLp?&Ifo2AQY?BTa_$5fPz)=w2qxTAcf><^OQfG)|8RhgyG`DH`|Zc|v(mgieZ^lO zG>6=V(-g1q3?*zi%XD8<%P~RkieTQmsXrEhjl)De?&umz>WJtzO&W9#6;T~(?!B^l z7N8RW(E95gzJ<%8I}_{4Fz!vF$5xU+x0DE4cOncgkQM3#rvACx9FGviL%TK(Bsc?W z=m|hnBTIk^NtB=86n>NJ#r&%P!s}zYsm7fCvGQ9&$(cibQxJiN5=n#6yM#e~PsIJ% z-7tAh!jbTPBpxJ#k#xsMIw-Wolx%><0(4>kdZWYVdP#K0M0WaEOQI1XAl^v7oCu?y zBp6vpWD4E;Z3#>zp?o}nfv81|o(QM(b&3QGV^RL{rid>DkZMBsPDR_bmg_R0TVF!S2U&nl3_zQ2$b9ar z;yYxoA>(>7O^W5my~z*=X`~N}wAUnKNJ|2hEeZ3YM39ZeqnC)qP!kP}-HA}u9_`0z z_x}pejT<+^6t$nFs${`beNQMkQIJ{v?$pI0Gzm=gVz((7dd$hp zRcTCwPJIlm9M6Kj@lvX5h<8s=AI<`FA^`fP%;&i#zQb%-wUJC(r9V_bM90gn|ZkA@NZI^!V0InS1#a@jp-P zkbbOW%Dj<6KM2r-H_#1gSV_ZBmIh>76w3pD?`V;Teb;Yk`W}bWI;Q$2+y{0^^v6IV^^Zwy~ z&GVyhJ?Qpt1giE{uC|Hi_xM*VzHGnQmr6FWqD&j9G)bb#IUL73V2rPl9;{4eV70C+ zdT>6I>bIsJc8n&{E$X!>YNIS`B_qbw+<%<#W-#ZYlOwLp7jkIRdKNw5CCWzS(&WUN@s%Q4?=GT892`n* z%|C1(P5hB$D8|{?A2=oPp|!|Im4c*6=QBX>3_m0}&kot?0;bNlz_hmw?)4=8%Bj9^|2)x%-l>4&$uGLrb*M_As?Wi*^L#K;> z>cMp;)=q{oQ{ldexCpf+rE+LCcXVXFXhY}Z{n41K;?Kt2;@d_~%{#`W`ahX)SMTaw z6_%m7vKF-^H|5uPo`*wZe{5hc?`h1fBFrnf+?maConx9eKDhUYk1t&?le zx%hg$i`S#I7w}ZJoTy*w`uvRs^f>>5qugk~9vWm`&(5}Pl~&H}i&|Y*>N_0!H8r-~ z%6yBD6aJaUVc|L1_8T_i{oYak)&FTgA8arBLZ6N8x?qhp)>vbWHP%>TjWyO-V~sV| hSYwSf)_#}mzX1?*L1=s(Zeaib002ovPDHLkV1l*eI%og@ diff --git a/euphony/src/main/res/values-sw600dp/dimens.xml b/euphony/src/main/res/values-sw600dp/dimens.xml deleted file mode 100644 index 44f01db..0000000 --- a/euphony/src/main/res/values-sw600dp/dimens.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/euphony/src/main/res/values-sw720dp-land/dimens.xml b/euphony/src/main/res/values-sw720dp-land/dimens.xml deleted file mode 100644 index ee04b4b..0000000 --- a/euphony/src/main/res/values-sw720dp-land/dimens.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/euphony/src/main/res/values/dimens.xml b/euphony/src/main/res/values/dimens.xml deleted file mode 100644 index 0e9ce62..0000000 --- a/euphony/src/main/res/values/dimens.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/euphony/src/main/res/values/strings.xml b/euphony/src/main/res/values/strings.xml deleted file mode 100644 index 1805e66..0000000 --- a/euphony/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - Euphony - - diff --git a/euphony/src/main/res/values/styles.xml b/euphony/src/main/res/values/styles.xml deleted file mode 100644 index 6ce89c7..0000000 --- a/euphony/src/main/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - diff --git a/euphony/src/test/java/euphony/lib/receiver/ReceiverUnitTest.java b/euphony/src/test/java/euphony/lib/receiver/ReceiverUnitTest.java index 1b6268f..8851e31 100644 --- a/euphony/src/test/java/euphony/lib/receiver/ReceiverUnitTest.java +++ b/euphony/src/test/java/euphony/lib/receiver/ReceiverUnitTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import euphony.lib.transmitter.EuDataEncoder; +import co.euphony.rx.EuDataDecoder; import static org.junit.Assert.assertEquals; diff --git a/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java b/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java index eddaca6..a20e14c 100644 --- a/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java +++ b/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java @@ -1,8 +1,9 @@ package euphony.lib.transmitter; import org.junit.Test; -import euphony.lib.transmitter.EuDataEncoder; -import euphony.lib.util.EuOption; +import co.euphony.tx.EuDataEncoder; +import co.euphony.tx.EuTxManager; +import co.euphony.util.EuOption; import static org.junit.Assert.assertEquals; public class TransmitterUnitTest { diff --git a/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java b/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java index a0de913..879df99 100644 --- a/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java +++ b/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java @@ -1,7 +1,10 @@ package euphony.lib.util; import org.junit.Test; -import euphony.lib.transmitter.EuDataEncoder; + +import co.euphony.util.ErrorHandler; +import co.euphony.util.EuOption; +import co.euphony.util.PacketErrorDetector; import static org.junit.Assert.assertEquals; public class UtilUnitTest { diff --git a/settings.gradle b/settings.gradle index 83ff1a7..08159e5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1 @@ -include ':euphony' - -// select main language : java, kotlin -gradle.ext.language = "java" // or kotlin \ No newline at end of file +include ':euphony' \ No newline at end of file From 849c8666e0cc0482af23db1817d9ce179d49ba46 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 05:03:38 +0900 Subject: [PATCH 02/31] Refactoring COMMON interface > Renamed Constants from COMMON > Constantization all variables (public static final) --- .../java/co/euphony/common/Constants.java | 22 +++++++++++++++++++ .../main/java/co/euphony/rx/EuFreqObject.java | 20 ++++++++--------- .../main/java/co/euphony/rx/EuRxManager.java | 4 ++-- .../java/co/euphony/tx/EuFreqGenerator.java | 12 +++++----- .../src/main/java/co/euphony/tx/EuPlayer.java | 4 ++-- .../src/main/java/co/euphony/util/COMMON.java | 21 ------------------ 6 files changed, 42 insertions(+), 41 deletions(-) create mode 100644 euphony/src/main/java/co/euphony/common/Constants.java delete mode 100644 euphony/src/main/java/co/euphony/util/COMMON.java diff --git a/euphony/src/main/java/co/euphony/common/Constants.java b/euphony/src/main/java/co/euphony/common/Constants.java new file mode 100644 index 0000000..b7f546e --- /dev/null +++ b/euphony/src/main/java/co/euphony/common/Constants.java @@ -0,0 +1,22 @@ +package co.euphony.common; + +public class Constants { + + // RX & TX COMMON VARIABLES + public static final int SAMPLERATE = 44100; + public static final int FFT_SIZE = 512; + public static final int DATA_LENGTH = FFT_SIZE * 4; + public static final int FADE_RANGE = DATA_LENGTH / 16; + + public static final double MAX_FREQ = 22050.0; + public static final int START_FREQ = 18000; + public static final int CHANNEL = 16; + public static final int CHANNEL_SPAN = SAMPLERATE / FFT_SIZE; // Frequency Interval + public static final int BUNDLE_INTERVAL = CHANNEL_SPAN * CHANNEL; + + // RX + public static final int MAX_REF = 4000; + public static final int MIN_REF = 50; + + public static final int DEFAULT_REF = 500; // BASE Reference value +} diff --git a/euphony/src/main/java/co/euphony/rx/EuFreqObject.java b/euphony/src/main/java/co/euphony/rx/EuFreqObject.java index ecd340c..8376d52 100644 --- a/euphony/src/main/java/co/euphony/rx/EuFreqObject.java +++ b/euphony/src/main/java/co/euphony/rx/EuFreqObject.java @@ -7,21 +7,21 @@ import java.util.ArrayList; import android.util.Log; -import co.euphony.util.COMMON; +import co.euphony.common.Constants; import co.euphony.util.PacketErrorDetector; public class EuFreqObject { - public final int SAMPLERATE = COMMON.SAMPLERATE;//44100; - public final int fftsize = COMMON.FFT_SIZE;//512; - public final int MAXREFERENCE = COMMON.MAX_REF;//4000; - public final int MINREFERENCE = COMMON.MIN_REF;//50; - public final double MAXFREQUENCY = COMMON.MAX_FREQ;//22050.0; - public final int DEFAULT_REF = COMMON.DEFAULT_REF;//500 - public final int START_FREQ = COMMON.START_FREQ; //18000 - public final int RXCHANNEL = COMMON.CHANNEL;// 16 + public final int SAMPLERATE = Constants.SAMPLERATE;//44100; + public final int fftsize = Constants.FFT_SIZE;//512; + public final int MAXREFERENCE = Constants.MAX_REF;//4000; + public final int MINREFERENCE = Constants.MIN_REF;//50; + public final double MAXFREQUENCY = Constants.MAX_FREQ;//22050.0; + public final int DEFAULT_REF = Constants.DEFAULT_REF;//500 + public final int START_FREQ = Constants.START_FREQ; //18000 + public final int RXCHANNEL = Constants.CHANNEL;// 16 private final int STARTCHANNEL = 1; - private int mFreqSpan = COMMON.CHANNEL_SPAN; // 86 + private int mFreqSpan = Constants.CHANNEL_SPAN; // 86 public final int START_BIT = START_FREQ - mFreqSpan; public final int START_BIT_IDX = RXCHANNEL; public int[] DATA_FREQ = new int[RXCHANNEL]; diff --git a/euphony/src/main/java/co/euphony/rx/EuRxManager.java b/euphony/src/main/java/co/euphony/rx/EuRxManager.java index 54f1321..ad604c5 100644 --- a/euphony/src/main/java/co/euphony/rx/EuRxManager.java +++ b/euphony/src/main/java/co/euphony/rx/EuRxManager.java @@ -1,6 +1,6 @@ package co.euphony.rx; -import co.euphony.util.COMMON; +import co.euphony.common.Constants; import android.os.Handler; import android.os.Message; import android.util.Log; @@ -165,7 +165,7 @@ public void run() { while(!startswt) { processFFT(); int i; - for(i = 21000; i >= 16500; i-=COMMON.CHANNEL_SPAN) + for(i = 21000; i >= 16500; i-= Constants.CHANNEL_SPAN) if(100 < detectFreq(i)){ startswt = true; break; diff --git a/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java b/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java index 69577ab..8d429d1 100644 --- a/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java +++ b/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java @@ -1,19 +1,19 @@ package co.euphony.tx; -import co.euphony.util.COMMON; +import co.euphony.common.Constants; public class EuFreqGenerator { // FIXED ACOUSTIC DATA - public final int SAMPLERATE = COMMON.SAMPLERATE;//44100; - public final int DATA_LENGTH = COMMON.DATA_LENGTH;//2048; + public final int SAMPLERATE = Constants.SAMPLERATE;//44100; + public final int DATA_LENGTH = Constants.DATA_LENGTH;//2048; public final double PI = Math.PI; public final double PI2 = PI * 2; // Member for Frequency point // DEFAULT DEFINITION - private int mFreqBasePoint = COMMON.START_FREQ; - private int mFreqSpan = COMMON.CHANNEL_SPAN; //86 + private int mFreqBasePoint = Constants.START_FREQ; + private int mFreqSpan = Constants.CHANNEL_SPAN; //86 private short[] mZeroSource = new short[DATA_LENGTH]; public EuFreqGenerator() { } @@ -57,7 +57,7 @@ public short[] makeFrequencyWithValue(int value) { public short[] applyCrossFade(short[] source) { double mini_window; - int fade_section = COMMON.FADE_RANGE; + int fade_section = Constants.FADE_RANGE; for(int i = 0; i < fade_section; i++) { mini_window = (double)i / (double)fade_section; diff --git a/euphony/src/main/java/co/euphony/tx/EuPlayer.java b/euphony/src/main/java/co/euphony/tx/EuPlayer.java index ca7624b..fe2fa80 100644 --- a/euphony/src/main/java/co/euphony/tx/EuPlayer.java +++ b/euphony/src/main/java/co/euphony/tx/EuPlayer.java @@ -1,6 +1,6 @@ package co.euphony.tx; -import co.euphony.util.COMMON; +import co.euphony.common.Constants; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; @@ -9,7 +9,7 @@ public class EuPlayer { private short[] mSource; private AudioTrack mAudioTrack = null; - private final int DATA_LENGTH = COMMON.FFT_SIZE * 4;//2048; + private final int DATA_LENGTH = Constants.FFT_SIZE * 4;//2048; private short[] mZeroSource = new short[DATA_LENGTH]; public EuPlayer() { } diff --git a/euphony/src/main/java/co/euphony/util/COMMON.java b/euphony/src/main/java/co/euphony/util/COMMON.java deleted file mode 100644 index 920309d..0000000 --- a/euphony/src/main/java/co/euphony/util/COMMON.java +++ /dev/null @@ -1,21 +0,0 @@ -package co.euphony.util; - -public interface COMMON { - // RX & TX COMMON VARIABLES - int SAMPLERATE = 44100; - int FFT_SIZE = 512; - int DATA_LENGTH = FFT_SIZE * 4; - int FADE_RANGE = DATA_LENGTH / 16; - - double MAX_FREQ = 22050.0; - int START_FREQ = 18000; - int CHANNEL = 16; - int CHANNEL_SPAN = SAMPLERATE / FFT_SIZE; // Frequency Interval - int BUNDLE_INTERVAL = CHANNEL_SPAN * CHANNEL; - - // RX - int MAX_REF = 4000; - int MIN_REF = 50; - - int DEFAULT_REF = 500; // BASE Reference value -} From b246cde5f54348f38470b3ecdf72d8c4f1fa73c3 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 05:52:50 +0900 Subject: [PATCH 03/31] Refactoring `EuOption` > Applied Builder Pattern > Rearrange Option's value > Renaming Option Types (Coding, Mode, Modulation) --- .../main/java/co/euphony/tx/EuCodeMaker.java | 91 ------------------- .../main/java/co/euphony/tx/EuTxManager.java | 31 +------ .../main/java/co/euphony/util/EuOption.java | 89 ++++++++++++++---- .../lib/transmitter/TransmitterUnitTest.java | 43 ++++++--- .../java/euphony/lib/util/UtilUnitTest.java | 38 ++++---- 5 files changed, 121 insertions(+), 171 deletions(-) diff --git a/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java b/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java index 85c3f4f..b9b8d96 100644 --- a/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java +++ b/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java @@ -117,95 +117,4 @@ public short[] assembleData(String data) return assembledData; } - - public short[] assembleLiveData(String data) - { - short[] liveStartData = applyCrossFade(makeStaticFrequency(START_BIT - getFreqSpan(), 0)); - short[] startData = applyCrossFade(makeStaticFrequency(START_BIT, 0)); - short[] assembledData = liveStartData.clone(); - int[] payload = new int[data.length() + 1]; - int payloadSum = 0; - - switch(mChannelMode) - { - case SINGLE: - for(int i = 0; i < data.length(); i++) - { - switch(data.charAt(i)) - { - case '0': - assembledData = appendRawData(assembledData, getZeroSource()); - break; - case '1': - assembledData = appendRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint(),0))); - break; - } - // checksum - assembledData = appendRawData(assembledData, makeFrequencyWithValue(PacketErrorDetector.makeCheckSum(data.charAt(i) - '0'))); - // parity check - assembledData = appendRawData(assembledData, makeFrequencyWithValue(PacketErrorDetector.makeParellelParity(data.charAt(i) - '0'))); - // final code - assembledData = appendRawData(assembledData, startData); - } - break; - - case MULTI: - for(int i = 0; i < data.length(); i++) - { - switch(data.charAt(i)) - { - case '0': - break; - case '1': - assembledData = mixingRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan()*(i+1), 0))); - break; - } - // checksum - assembledData = appendRawData(assembledData, makeFrequencyWithValue(PacketErrorDetector.makeCheckSum(data.charAt(i) - '0'))); - // parity check - assembledData = appendRawData(assembledData, makeFrequencyWithValue(PacketErrorDetector.makeParellelParity(data.charAt(i) - '0'))); - // final code - assembledData = appendRawData(assembledData, startData); - } - - break; - - case EXINGLE: - for(int i = 0; i < data.length(); i++){ - char ch = data.charAt(i); - switch(ch) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - assembledData = appendRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan() * (ch - '0'), 0))); - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - assembledData = appendRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan() * (ch - 'a' + 10), 0))); - break; - } - // checksum - assembledData = appendRawData(assembledData, makeFrequencyWithValue(PacketErrorDetector.makeCheckSum(ch - '0'))); - // parity check - assembledData = appendRawData(assembledData, makeFrequencyWithValue(PacketErrorDetector.makeParellelParity(ch - '0'))); - // final code - assembledData = appendRawData(assembledData, startData); - } - break; - } - - return assembledData; - } } diff --git a/euphony/src/main/java/co/euphony/tx/EuTxManager.java b/euphony/src/main/java/co/euphony/tx/EuTxManager.java index 6f9cc4e..a37041e 100644 --- a/euphony/src/main/java/co/euphony/tx/EuTxManager.java +++ b/euphony/src/main/java/co/euphony/tx/EuTxManager.java @@ -10,7 +10,6 @@ public class EuTxManager { private AudioTrack mAudioTrack = null; - private EuOption mTxOption = null; private EuCodeMaker mCodeMaker = new EuCodeMaker(); private EuDataEncoder mDataEncoder = new EuDataEncoder(); @@ -20,14 +19,7 @@ public short[] getOutStream() { private short[] mOutStream; - public EuTxManager() { - mTxOption = new EuOption(EuOption.EncodingType.ASCII, EuOption.CommunicationMode.GENERAL); - } - - public EuTxManager(EuOption option) { - mTxOption = option; - } - + public EuTxManager() { } /* * @deprecated Replaced by {@link #setCode()}, deprecated for naming & dynamic option. */ @@ -39,25 +31,8 @@ public void euInitTransmit(String data) { public void setCode(String data) { String code = data; - - // set encoding code. - switch(mTxOption.getEncodingType()) { - case ASCII: - code = mDataEncoder.encodeStaticHexCharSource(data); - break; - case HEX: - break; - } - - // set communication mode. - switch(mTxOption.getCommunicationMode()) { - case GENERAL: - mOutStream = mCodeMaker.assembleData(code); - break; - case LIVE: - mOutStream = mCodeMaker.assembleLiveData(code); - break; - } + code = mDataEncoder.encodeStaticHexCharSource(data); + mOutStream = mCodeMaker.assembleData(code); } public void process() { process(1); } diff --git a/euphony/src/main/java/co/euphony/util/EuOption.java b/euphony/src/main/java/co/euphony/util/EuOption.java index 887ef7d..faa8e38 100644 --- a/euphony/src/main/java/co/euphony/util/EuOption.java +++ b/euphony/src/main/java/co/euphony/util/EuOption.java @@ -1,37 +1,88 @@ package co.euphony.util; public class EuOption { - public enum EncodingType { - ASCII, HEX + public enum CodingType { + BASE2, BASE16 } - public enum CommunicationMode { - GENERAL, - LIVE + public enum ModeType { + DEFAULT, + EUPI, + DETECT } - private EncodingType mEncodingType; - private CommunicationMode mCommunicationMode; + public enum ModulationType { + FSK, + /* + TODO: v0.7.1.6 had ASK feature. but v0.8 has to create it. + ASK, + */ + /* + TODO: Rearchitecturing necessary because the CPFSK modulation type has some glitch sound. + CPFSK + */ + } + + private CodingType mCodingType; + private ModeType mModeType; + private ModulationType mModulationType; + + public EuOption(CodingType codingType, ModeType modeType, ModulationType modulationType) { + mCodingType = codingType; + mModeType = modeType; + mModulationType = modulationType; + } + + public CodingType getCodingType() { + return mCodingType; + } + + public void setCodingType(CodingType mCodingType) { + this.mCodingType = mCodingType; + } + + public ModeType getMode() { + return mModeType; + } - public EuOption() {} - public EuOption(EncodingType _encodingType, CommunicationMode _commMode) { - mEncodingType = _encodingType; - mCommunicationMode = _commMode; + public void setMode(ModeType mModeType) { + this.mModeType = mModeType; } - public EncodingType getEncodingType() { - return mEncodingType; + public ModulationType getModulationType() { + return mModulationType; } - public void setEncodingType(EncodingType mEncodingType) { - this.mEncodingType = mEncodingType; + public void setModulationType(ModulationType mModulationType) { + this.mModulationType = mModulationType; } - public CommunicationMode getCommunicationMode() { - return mCommunicationMode; + public static Builder builder() { + return new Builder(); } - public void setCommunicationMode(CommunicationMode mCommunicationMode) { - this.mCommunicationMode = mCommunicationMode; + public static class Builder { + private CodingType codingType; + private ModeType commMode; + private ModulationType modulationType; + + public Builder encodingWith(CodingType type) { + codingType = type; + return this; + } + + public Builder modeWith(ModeType mode) { + commMode = mode; + return this; + } + + public Builder modulationWith(ModulationType type) { + modulationType = type; + return this; + } + + public EuOption build() { + return new EuOption(codingType, commMode, modulationType); + } } } diff --git a/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java b/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java index a20e14c..a42b253 100644 --- a/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java +++ b/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java @@ -1,5 +1,6 @@ package euphony.lib.transmitter; +import org.junit.Ignore; import org.junit.Test; import co.euphony.tx.EuDataEncoder; import co.euphony.tx.EuTxManager; @@ -8,29 +9,45 @@ import static org.junit.Assert.assertEquals; public class TransmitterUnitTest { @Test + @Ignore("This unit-test was substituted by Base16EncoderTest.cpp on gtest") public void encode_hex_isCorrect() { + /* + * This unit-test was substituted by Base16EncoderTest.cpp on gtest + * EuDataEncoder mEuDataEncoder = new EuDataEncoder("hell"); assertEquals(mEuDataEncoder.getOriginalSource(), "hell"); assertEquals(mEuDataEncoder.encodeHexCharSource(), "68656c6c"); assertEquals(EuDataEncoder.encodeStaticHexCharSource("hello, euphony"), "68656c6c6f2c20657570686f6e79"); + */ } @Test - public void EuTxManager_iscorrect() { - EuTxManager mEuTxManager = new EuTxManager(); - mEuTxManager.setCode("Hello, Euphony"); - int length = 63488; - int outStreamLength = mEuTxManager.getOutStream().length; - assertEquals(outStreamLength, length); - mEuTxManager.process(); - mEuTxManager.process(3); + @Ignore("This unit-test was substituted by FSKTest.cpp on gtest") + public void EuCodeMaker_iscorrect() { + /* + * This unit-test was substituted by FSKTest.cpp on gtest + * + EuOption txOption = new EuOption(); + txOption.setModulationType(EuOption.ModulationType.CPFSK); - EuTxManager mEuTxManager2 = new EuTxManager(new EuOption(EuOption.EncodingType.ASCII, EuOption.CommunicationMode.LIVE)); - mEuTxManager2.setCode("Hello, Euphony"); - length = 231424; - outStreamLength = mEuTxManager2.getOutStream().length; - assertEquals(outStreamLength, length); + String code = EuDataEncoder.encodeStaticHexCharSource("hello"); + EuCodeMaker mCodeMaker = new EuCodeMaker(txOption); + short[] musicSource = mCodeMaker.assembleData(code); + assertEquals(musicSource.length, 5 * 2 * 2048); + + txOption.setModulationType(EuOption.ModulationType.FSK); + mCodeMaker.setOption(txOption); + musicSource = mCodeMaker.assembleData(code); + assertEquals(musicSource.length, 5 * 2 * 2048); + + txOption.setModulationType(EuOption.ModulationType.ASK); + mCodeMaker.setOption(txOption); + code = EuDataEncoder.encodeStaticBinaryCharSource("hello"); + musicSource = mCodeMaker.assembleData(code); + assertEquals(musicSource.length, 5*2*4*2048); + + */ } } diff --git a/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java b/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java index 879df99..8c54b93 100644 --- a/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java +++ b/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java @@ -7,6 +7,9 @@ import co.euphony.util.PacketErrorDetector; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + public class UtilUnitTest { @Test public void packet_err_detect_isCorrect() @@ -16,19 +19,19 @@ public void packet_err_detect_isCorrect() assertEquals(PacketErrorDetector.makeCheckSum( 234), 6); assertEquals(PacketErrorDetector.makeParellelParity(source), 4); assertEquals(PacketErrorDetector.makeParellelParity(234), 10); - assertEquals(PacketErrorDetector.checkEvenParity(source, 4), true); - assertEquals(PacketErrorDetector.checkEvenParity(source, 5), false); - assertEquals(PacketErrorDetector.verifyCheckSum(source, 14), true); - assertEquals(PacketErrorDetector.verifyCheckSum(source, 13), false); + assertTrue(PacketErrorDetector.checkEvenParity(source, 4)); + assertFalse(PacketErrorDetector.checkEvenParity(source, 5)); + assertTrue(PacketErrorDetector.verifyCheckSum(source, 14)); + assertFalse(PacketErrorDetector.verifyCheckSum(source, 13)); PacketErrorDetector errorDetector = new PacketErrorDetector(); - assertEquals(errorDetector.euGetEvenParityState(), false); + assertFalse(errorDetector.euGetEvenParityState()); errorDetector.euSetEvenParityState(true); - assertEquals(errorDetector.euGetEvenParityState(), true); + assertTrue(errorDetector.euGetEvenParityState()); } @Test - public void error_handler_isCorret() + public void error_handler_isCorrect() { ErrorHandler mErrorHandler = new ErrorHandler(20); mErrorHandler.euSetChannelState(ErrorHandler.BUSY); @@ -43,19 +46,14 @@ public void error_handler_isCorret() @Test public void option_isCorrect() { - EuOption option = new EuOption(); - option.setEncodingType(EuOption.EncodingType.ASCII); - assertEquals(option.getEncodingType(), EuOption.EncodingType.ASCII); - option.setEncodingType(EuOption.EncodingType.HEX); - assertEquals(option.getEncodingType(), EuOption.EncodingType.HEX); - option.setCommunicationMode(EuOption.CommunicationMode.GENERAL); - assertEquals(option.getCommunicationMode(), EuOption.CommunicationMode.GENERAL); - option.setCommunicationMode(EuOption.CommunicationMode.LIVE); - assertEquals(option.getCommunicationMode(), EuOption.CommunicationMode.LIVE); - - EuOption option2 = new EuOption(EuOption.EncodingType.HEX, EuOption.CommunicationMode.LIVE); - assertEquals(option2.getEncodingType(), EuOption.EncodingType.HEX); - assertEquals(option2.getCommunicationMode(), EuOption.CommunicationMode.LIVE); + EuOption option = EuOption.builder() + .modeWith(EuOption.ModeType.DEFAULT) + .encodingWith(EuOption.CodingType.BASE16) + .modulationWith(EuOption.ModulationType.FSK) + .build(); + + assertEquals(option.getCodingType(), EuOption.CodingType.BASE16); + assertEquals(option.getMode(), EuOption.ModeType.DEFAULT); } } From 37b82cab3e9a21ac7284c5315ae89d5a097fd69d Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 06:11:39 +0900 Subject: [PATCH 04/31] Prepare Unit-test of Native Code using gtest --- euphony/src/main/cpp/CMakeLists.txt | 27 +++++++++- euphony/src/main/cpp/tests/CMakeLists.txt | 50 +++++++++++++++++++ .../src/main/cpp/tests/cmake.run.test.script | 14 ++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 euphony/src/main/cpp/tests/CMakeLists.txt create mode 100644 euphony/src/main/cpp/tests/cmake.run.test.script diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index dd0972d..91652e6 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.4.1) project(euphony VERSION 0.8) -#Legacy JNI +# Legacy JNI set(LEGACY_SRC arms/kiss_fft.c arms/kiss_fftr.c @@ -19,3 +19,28 @@ target_link_libraries( -fopenmp -static-openmp ) + +# Rearchitectured Euphony Native +set(EUPHONY_SRC + arms/kiss_fft.c + arms/kiss_fftr.c + ) + +set(EUPHONY_HDR core) +set(DEBUG_HELPER debug-helper) +set(DEBUG_HELPER_SRC debug-helper/Trace.cpp) + +include_directories( ${EUPHONY_HDR} ${DEBUG_HELPER}) + +add_library(euphony SHARED ${EUPHONY_SRC} ${DEBUG_HELPER_SRC}) +target_compile_definitions(euphony PUBLIC FIXED_POINT=16) + +target_link_libraries( + euphony + ${log-lib} + -fopenmp + -static-openmp +) + +# for c++ unit-test (gtest) +# add_subdirectory(tests) \ No newline at end of file diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt new file mode 100644 index 0000000..d23c11b --- /dev/null +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.4.1) + +set(TEST_EUPHONY testEuphony) + +# Include GoogleTest library +set (GOOGLETEST_ROOT ${ANDROID_NDK}/sources/third_party/googletest) +add_library (gtest STATIC ${GOOGLETEST_ROOT}/src/gtest_main.cc ${GOOGLETEST_ROOT}/src/gtest-all.cc) +target_include_directories (gtest PRIVATE ${GOOGLETEST_ROOT}) +target_include_directories (gtest PUBLIC ${GOOGLETEST_ROOT}/include) + +add_executable( + ${TEST_EUPHONY} +) + +target_link_libraries(${TEST_EUPHONY} PUBLIC euphony gtest) + +set(TARGET_TEST_DIR /data/local/tmp/${TEST_EUPHONY}) # Directory on device to push tests. +set(TARGET_TEST_LIB_DIR ${TARGET_TEST_DIR}/${ANDROID_ABI}) +set(LIBCPP_SHARED_PATH ${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/${ANDROID_ABI}/libc++_shared.so) + +find_program(ADB NAMES adb PATHS ${ANDROID_SDK_ROOT}/platform-tools) # Verified to be working on Linux. + +execute_process(COMMAND ${ADB} shell getprop ro.product.cpu.abi + OUTPUT_VARIABLE ADB_DEVICE_ABI + RESULT_VARIABLE ADB_DEVICE_ABI_RESULT) + +if( ${ADB_DEVICE_ABI_RESULT} ) + message("NO ABI OR MORE THAN ONE DEVICE/EMULATOR") +else() + string(STRIP ${ADB_DEVICE_ABI} ADB_DEVICE_ABI) + if(${ANDROID_ABI} STREQUAL ${ADB_DEVICE_ABI}) + # Prepare gtest for unit-test + add_custom_command(TARGET ${TEST_EUPHONY} POST_BUILD + COMMAND ${ADB} shell mkdir -p ${TARGET_TEST_LIB_DIR} + + # Push necessary libraries + COMMAND ${ADB} push $ ${TARGET_TEST_LIB_DIR}/ + COMMAND ${ADB} push ${LIBCPP_SHARED_PATH} ${TARGET_TEST_LIB_DIR}/ + + # Push Euphony Test Binary + COMMAND ${ADB} push $ ${TARGET_TEST_LIB_DIR}/ + COMMAND ${ADB} shell chmod 755 ${TARGET_TEST_LIB_DIR}/${TEST_EUPHONY} + ) + + # Run gtest & get the result. + add_custom_command(TARGET ${TEST_EUPHONY} POST_BUILD + COMMAND ${CMAKE_COMMAND} -DADB:STRING="${ADB}" -DTARGET_TEST_LIB_DIR:STRING="${TARGET_TEST_LIB_DIR}" -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake.run.test.script + ) + endif() +endif() \ No newline at end of file diff --git a/euphony/src/main/cpp/tests/cmake.run.test.script b/euphony/src/main/cpp/tests/cmake.run.test.script new file mode 100644 index 0000000..252bd54 --- /dev/null +++ b/euphony/src/main/cpp/tests/cmake.run.test.script @@ -0,0 +1,14 @@ +execute_process(COMMAND ${ADB} shell LD_LIBRARY_PATH=${TARGET_TEST_LIB_DIR} ${TARGET_TEST_LIB_DIR}/testEuphony + OUTPUT_VARIABLE GTEST_EUPHONY_OUTPUT + RESULT_VARIABLE GTEST_RESULT + ERROR_VARIABLE GTEST_ERROR_OUTPUT + ) + +if( ${GTEST_RESULT} GREATER 0 ) + string(STRIP ${GTEST_EUPHONY_OUTPUT} GTEST_EUPHONY_OUTPUT) + string(REPLACE "\n\n" "\r[NEXT]\r" GTEST_EUPHONY_OUTPUT ${GTEST_EUPHONY_OUTPUT}) + string(REPLACE "\n" "\r" GTEST_EUPHONY_OUTPUT ${GTEST_EUPHONY_OUTPUT}) + message(FATAL_ERROR "** Gtest Failure (${GTEST_RESULT}) **\r${GTEST_EUPHONY_OUTPUT}") +else() + message("**** Gtest Success ****") +endif() \ No newline at end of file From 9b16fb06b1452355a0966b7e5fc8d6adb9b4bf4e Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 06:20:32 +0900 Subject: [PATCH 05/31] Added cpp debugging helper --- euphony/src/main/cpp/debug-helper/Log.h | 22 +++++++ euphony/src/main/cpp/debug-helper/Trace.cpp | 73 +++++++++++++++++++++ euphony/src/main/cpp/debug-helper/Trace.h | 22 +++++++ 3 files changed, 117 insertions(+) create mode 100644 euphony/src/main/cpp/debug-helper/Log.h create mode 100644 euphony/src/main/cpp/debug-helper/Trace.cpp create mode 100644 euphony/src/main/cpp/debug-helper/Trace.h diff --git a/euphony/src/main/cpp/debug-helper/Log.h b/euphony/src/main/cpp/debug-helper/Log.h new file mode 100644 index 0000000..f1c5a25 --- /dev/null +++ b/euphony/src/main/cpp/debug-helper/Log.h @@ -0,0 +1,22 @@ +// +// Created by desig on 2020-07-30. +// + +#ifndef EUPHONY_LOG_H +#define EUPHONY_LOG_H +#include + +#define LOG_TAG "NDK_TEST" +#define LOGUNK(...) __android_log_print(ANDROID_LOG_UNKNOWN,LOG_TAG,__VA_ARGS__) +#define LOGDEF(...) __android_log_print(ANDROID_LOG_DEFAULT,LOG_TAG,__VA_ARGS__) +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) +#define LOGF(...) __android_log_print(ANDROID_FATAL_ERROR,LOG_TAG,__VA_ARGS__) +#define LOGS(...) __android_log_print(ANDROID_SILENT_ERROR,LOG_TAG,__VA_ARGS__) + +#define ASSERT(cond, ...) if (!(cond)) {__android_log_assert(#cond, MODULE_NAME, __VA_ARGS__);} + +#endif //EUPHONY_LOG_H diff --git a/euphony/src/main/cpp/debug-helper/Trace.cpp b/euphony/src/main/cpp/debug-helper/Trace.cpp new file mode 100644 index 0000000..60ea454 --- /dev/null +++ b/euphony/src/main/cpp/debug-helper/Trace.cpp @@ -0,0 +1,73 @@ +// +// Created by opener on 20. 8. 24. +// + +#include +#include "Log.h" +#include +#include "Trace.h" + +static const int TRACE_MAX_SECTION_NAME_LENGTH = 100; + +// Tracing functions +static void *(*ATrace_beginSection)(const char *sectionName); + +static void *(*ATrace_endSection)(void); + +static bool *(*ATrace_isEnabled)(void); + +typedef void *(*fp_ATrace_beginSection)(const char *sectionName); + +typedef void *(*fp_ATrace_endSection)(void); + +typedef bool *(*fp_ATrace_isEnabled)(void); + +bool Trace::is_enabled_ = false; +bool Trace::has_error_been_shown_ = false; + +void Trace::beginSection(const char *fmt, ...) { + + if (is_enabled_) { + static char buff[TRACE_MAX_SECTION_NAME_LENGTH]; + va_list args; + va_start(args, fmt); + vsprintf(buff, fmt, args); + va_end(args); + ATrace_beginSection(buff); + } else if (!has_error_been_shown_) { + LOGE("Tracing is either not initialized (call Trace::initialize()) " + "or not supported on this device"); + has_error_been_shown_ = true; + } +} + +void Trace::endSection() { + + if (is_enabled_) { + ATrace_endSection(); + } +} + +void Trace::initialize() { + + // Using dlsym allows us to use tracing on API 21+ without needing android/trace.h which wasn't + // published until API 23 + void *lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL); + if (lib == nullptr) { + LOGE("Could not open libandroid.so to dynamically load tracing symbols"); + } else { + ATrace_beginSection = + reinterpret_cast( + dlsym(lib, "ATrace_beginSection")); + ATrace_endSection = + reinterpret_cast( + dlsym(lib, "ATrace_endSection")); + ATrace_isEnabled = + reinterpret_cast( + dlsym(lib, "ATrace_isEnabled")); + + if (ATrace_isEnabled != nullptr && ATrace_isEnabled()) { + is_enabled_ = true; + } + } +} \ No newline at end of file diff --git a/euphony/src/main/cpp/debug-helper/Trace.h b/euphony/src/main/cpp/debug-helper/Trace.h new file mode 100644 index 0000000..1d8d99a --- /dev/null +++ b/euphony/src/main/cpp/debug-helper/Trace.h @@ -0,0 +1,22 @@ +// +// Created by opener on 20. 8. 24. +// + +#ifndef EUPHONY_TRACE_H +#define EUPHONY_TRACE_H + + +class Trace { +public: + static void beginSection(const char *format, ...); + static void endSection(); + static bool isEnabled() { return is_enabled_; } + static void initialize(); + +private: + static bool is_enabled_; + static bool has_error_been_shown_; +}; + + +#endif //EUPHONY_TRACE_H From 902a96c1650c8776ff54d01db4ba7c2bc85125ec Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 06:41:15 +0900 Subject: [PATCH 06/31] Added `Wave` concept that include oscillator functions > Applied WaveBuilder > Unit-test were passed! (Wave, WaveBuilder) --- euphony/src/main/cpp/CMakeLists.txt | 4 +- euphony/src/main/cpp/core/Definitions.h | 56 +++++++ euphony/src/main/cpp/core/Wave.h | 55 +++++++ euphony/src/main/cpp/core/WaveBuilder.h | 18 +++ euphony/src/main/cpp/core/source/Wave.cpp | 143 ++++++++++++++++++ .../src/main/cpp/core/source/WaveBuilder.cpp | 32 ++++ euphony/src/main/cpp/tests/CMakeLists.txt | 2 + .../src/main/cpp/tests/waveBuilderTest.cpp | 45 ++++++ euphony/src/main/cpp/tests/waveTest.cpp | 89 +++++++++++ 9 files changed, 443 insertions(+), 1 deletion(-) create mode 100644 euphony/src/main/cpp/core/Definitions.h create mode 100644 euphony/src/main/cpp/core/Wave.h create mode 100644 euphony/src/main/cpp/core/WaveBuilder.h create mode 100644 euphony/src/main/cpp/core/source/Wave.cpp create mode 100644 euphony/src/main/cpp/core/source/WaveBuilder.cpp create mode 100644 euphony/src/main/cpp/tests/waveBuilderTest.cpp create mode 100644 euphony/src/main/cpp/tests/waveTest.cpp diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index 91652e6..9127ecb 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -24,6 +24,8 @@ target_link_libraries( set(EUPHONY_SRC arms/kiss_fft.c arms/kiss_fftr.c + core/source/Wave.cpp + core/source/WaveBuilder.cpp ) set(EUPHONY_HDR core) @@ -43,4 +45,4 @@ target_link_libraries( ) # for c++ unit-test (gtest) -# add_subdirectory(tests) \ No newline at end of file +add_subdirectory(tests) \ No newline at end of file diff --git a/euphony/src/main/cpp/core/Definitions.h b/euphony/src/main/cpp/core/Definitions.h new file mode 100644 index 0000000..571b95e --- /dev/null +++ b/euphony/src/main/cpp/core/Definitions.h @@ -0,0 +1,56 @@ +#ifndef EUPHONY_DEFINITIONS_H +#define EUPHONY_DEFINITIONS_H + +#include +#include + +namespace Euphony { + + constexpr int32_t kChannelCount = 2; + constexpr int32_t kSampleRate = 44100; + constexpr int32_t kFFTSize = 512; + constexpr int32_t kBufferSize = 2048; + constexpr int32_t kBufferFadeLength = 256; + constexpr int32_t kFrequencyInterval = kSampleRate / kFFTSize; + constexpr int32_t kStandardFrequency = 18001; + constexpr int32_t kStartSignalFrequency = kStandardFrequency - kFrequencyInterval; + constexpr double kPi = M_PI; + constexpr double kTwoPi = kPi * 2.0; + + static constexpr const char* BASE16_EXCEPTION_MSG = "BASE16 couldn't support this code value"; + + enum class ModeType : int32_t { + DEFAULT = 0, // Default Soundless Communication + DETECT = 1, // For Wave Detection + EUPI = 2, // For EUPI Mode + /* + * TODO: DISTANCE CALCULATION (Distance = Speed * Time) + DISTANCE = 4 + */ + }; + + enum class ModulationType : int32_t { + FSK = 0, + /* + TODO: v0.7.1.6 had ASK feature. but v0.8 has to create it. + ASK, + */ + /* + TODO: Rearchitecturing necessary because the CPFSK modulation type has some glitch sound. + CPFSK + */ + }; + + enum class BaseType : int32_t { + BASE2 = 0, + BASE16 = 1, + }; + + enum class CharsetType : int32_t { + ASCII = 0, + /* TODO: Develop Charset for UTF8 */ + UTF8 = 1 + }; +} + +#endif \ No newline at end of file diff --git a/euphony/src/main/cpp/core/Wave.h b/euphony/src/main/cpp/core/Wave.h new file mode 100644 index 0000000..6a6fd04 --- /dev/null +++ b/euphony/src/main/cpp/core/Wave.h @@ -0,0 +1,55 @@ +// +// Created by designe on 20. 9. 16. +// + +#ifndef EUPHONY_WAVE_H +#define EUPHONY_WAVE_H + +#include + +namespace Euphony { + + class WaveBuilder; + + enum CrossfadeType { + FRONT, END, BOTH, NONE + }; + + class Wave { + public: + Wave(); + Wave(int hz, int bufferSize); + Wave(const float* src, int bufferSize); + explicit Wave(const Wave& copy); + + static WaveBuilder create(); + void oscillate(); + void oscillate(int hz, int size); + void setCrossfade(CrossfadeType crossfadeType); + + int getHz() const; + void setHz(int hz); + int getSize() const; + void setSize(int size); + + std::vector getSource() const; + void setSource(const std::vector &source); + std::vector getInt16Source(); + static int16_t convertFloat2Int16(float source); + + private: + friend class WaveBuilder; + + int mHz; + int mSize; + CrossfadeType crossfadeType; + std::vector mSource; + float mPhase = 0.0; + std::atomic mPhaseIncrement{0.0}; + void updatePhaseIncrement(int hz); + + }; + +} + +#endif //EUPHONY_WAVE_H diff --git a/euphony/src/main/cpp/core/WaveBuilder.h b/euphony/src/main/cpp/core/WaveBuilder.h new file mode 100644 index 0000000..d22be41 --- /dev/null +++ b/euphony/src/main/cpp/core/WaveBuilder.h @@ -0,0 +1,18 @@ +#ifndef EUPHONY_WAVEBUILDER_H +#define EUPHONY_WAVEBUILDER_H + +#include "Wave.h" + +namespace Euphony { + class WaveBuilder { + private: + Wave wave; + + public: + WaveBuilder& vibratesAt(int hz); + WaveBuilder& setSize(int size); + WaveBuilder& setCrossfade(CrossfadeType type); + std::shared_ptr build(); + }; +}; +#endif //EUPHONY_WAVEBUILDER_H diff --git a/euphony/src/main/cpp/core/source/Wave.cpp b/euphony/src/main/cpp/core/source/Wave.cpp new file mode 100644 index 0000000..3d1f9d6 --- /dev/null +++ b/euphony/src/main/cpp/core/source/Wave.cpp @@ -0,0 +1,143 @@ +// +// Created by designe on 20. 9. 16. +// + +#include "../Definitions.h" +#include "../Wave.h" +#include "../WaveBuilder.h" + +using namespace Euphony; + +Euphony::Wave::Wave() +: mHz(0), +mSize(0), +crossfadeType(NONE) +{} + +Euphony::Wave::Wave(int hz, int bufferSize) +: mHz(hz), +mSize(bufferSize), +crossfadeType(NONE) +{ + oscillate(); +} + +Wave::Wave(const float *src, int bufferSize) +: mHz(0) +, mSize(bufferSize) +, crossfadeType(NONE) +{ + for(int i = 0; i < bufferSize; ++i) { + mSource.push_back(src[i]); + } +} + +Euphony::Wave::Wave(const Wave& copy) +: mHz(copy.mHz), +mSize(copy.mSize), +crossfadeType(copy.crossfadeType) +{ + oscillate(); +} + +WaveBuilder Euphony::Wave::create() { + return WaveBuilder(); +} + +void Euphony::Wave::updatePhaseIncrement(int hz) { + mPhaseIncrement.store((kTwoPi * hz) / static_cast(kSampleRate)); +} + +void Euphony::Wave::oscillate() { + if(this->mHz > 0 && this->mSize > 0) { + updatePhaseIncrement(this->mHz); + + float phase = 0.0; + + for(int i = 0; i < this->mSize; ++i) { + mSource.push_back(sin(phase)); + phase += mPhaseIncrement; + if(phase > kTwoPi) phase -= kTwoPi; + } + + if(crossfadeType != NONE) { + for (int i = 0; i < kBufferFadeLength; ++i) { + float miniWindow = static_cast(i) / static_cast(kBufferFadeLength); + switch (crossfadeType) { + case BOTH: + mSource[i] *= miniWindow; + mSource[this->mSize - 1 - i] *= miniWindow; + break; + case END: + mSource[this->mSize - 1 - i] *= miniWindow; + break; + case FRONT: + mSource[i] *= miniWindow; + break; + default: + continue; + } + } + } + } +} + +void Euphony::Wave::oscillate(int hz, int size) { + this->setHz(hz); + this->setSize(size); + this->oscillate(); +} + +int Euphony::Wave::getHz() const { + return mHz; +} + +void Euphony::Wave::setHz(int hz) { + mHz = hz; + this->updatePhaseIncrement(hz); +} + +int Euphony::Wave::getSize() const { + return mSize; +} + +void Euphony::Wave::setSize(int size) { + mSize = size; + mSource.reserve(size); +} + +std::vector Euphony::Wave::getSource() const { + std::vector result; + result.reserve(mSource.capacity()); + result.assign(mSource.begin(), mSource.end()); + return result; +} + +std::vector Euphony::Wave::getInt16Source() { + std::vector result; + + if(mSource.empty()) { + return result; + } + + result.reserve(mSource.size()); + + for(float src : mSource) { + result.push_back(convertFloat2Int16(src)); + } + + return result; +} + +void Euphony::Wave::setSource(const std::vector &source) { + mSource = source; +} + +int16_t Euphony::Wave::convertFloat2Int16(float source) { + return static_cast(SHRT_MAX) * source; +} + +void Wave::setCrossfade(CrossfadeType crossfadeType) { + this->crossfadeType = crossfadeType; +} + diff --git a/euphony/src/main/cpp/core/source/WaveBuilder.cpp b/euphony/src/main/cpp/core/source/WaveBuilder.cpp new file mode 100644 index 0000000..213e52e --- /dev/null +++ b/euphony/src/main/cpp/core/source/WaveBuilder.cpp @@ -0,0 +1,32 @@ +#include "../WaveBuilder.h" + +using namespace Euphony; + +WaveBuilder& WaveBuilder::vibratesAt(int hz) { + wave.setHz(hz); + return *this; +} + +WaveBuilder& WaveBuilder::setSize(int size) { + wave.setSize(size); + return *this; +} + +WaveBuilder& WaveBuilder::setCrossfade(CrossfadeType type) { + wave.setCrossfade(type); + return *this; +} + +std::shared_ptr WaveBuilder::build() { + if(wave.getSize() > 0 && wave.getHz() > 0) { + wave.oscillate(); + } + + return std::make_shared(wave); +} + + + + + + diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index d23c11b..e1c922b 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -10,6 +10,8 @@ target_include_directories (gtest PUBLIC ${GOOGLETEST_ROOT}/include) add_executable( ${TEST_EUPHONY} + waveTest.cpp + waveBuilderTest.cpp ) target_link_libraries(${TEST_EUPHONY} PUBLIC euphony gtest) diff --git a/euphony/src/main/cpp/tests/waveBuilderTest.cpp b/euphony/src/main/cpp/tests/waveBuilderTest.cpp new file mode 100644 index 0000000..dece23c --- /dev/null +++ b/euphony/src/main/cpp/tests/waveBuilderTest.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +using std::string; +using namespace Euphony; + +typedef std::tuple TestParamType; + +class WaveBuilderTestFixture : public ::testing::TestWithParam { + +public: + std::shared_ptr wave = nullptr; +}; + +TEST_P(WaveBuilderTestFixture, WaveBuilderUnitTest) +{ +int inputHz; +int inputSize; + +std::tie(inputHz, inputSize) = GetParam(); + +wave = Wave::create() + .vibratesAt(inputHz) + .setSize(inputSize) + .build(); + +EXPECT_EQ(wave->getHz(), inputHz); +EXPECT_EQ(wave->getSize(), inputSize); +EXPECT_EQ(wave->getSource().size(), inputSize); +} + +INSTANTIATE_TEST_SUITE_P( + WaveBuilderTest, + WaveBuilderTestFixture, + ::testing::Values( + TestParamType(18000, 512), + TestParamType(18000, 1024), + TestParamType(18000, 2048), + TestParamType(20000, 2048), + TestParamType(21000, 2048) +)); diff --git a/euphony/src/main/cpp/tests/waveTest.cpp b/euphony/src/main/cpp/tests/waveTest.cpp new file mode 100644 index 0000000..a29ca3c --- /dev/null +++ b/euphony/src/main/cpp/tests/waveTest.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +using std::string; +using namespace Euphony; + +typedef std::tuple TestParamType; + +class WaveTestFixture : public ::testing::TestWithParam { + +public: + void makeFrequency() { + EXPECT_EQ(wave, nullptr); + wave = new Wave(); + ASSERT_NE(wave, nullptr); + } + + Wave* wave = nullptr; +}; + +TEST_P(WaveTestFixture, WaveUnitTest) +{ + makeFrequency(); + + int inputHz; + int inputSize; + + std::tie(inputHz, inputSize) = GetParam(); + + wave->setHz(inputHz); + EXPECT_EQ(wave->getHz(), inputHz); + wave->setSize(inputSize); + EXPECT_EQ(wave->getSize(), inputSize); + + auto source = wave->getSource(); + EXPECT_EQ(source.capacity(), inputSize); + EXPECT_EQ(source.size(), 0); + + EXPECT_EQ(wave->getSource().capacity(), inputSize); + EXPECT_EQ(wave->getSource().size(), 0); + + wave->oscillate(); + auto source2 = wave->getSource(); + EXPECT_EQ(source2.capacity(), inputSize); + EXPECT_EQ(source2.size(), inputSize); + + int zeroCount = 0; + for(float floatSrc : source2) { + if(floatSrc == 0) + zeroCount++; + else + break; + } + EXPECT_EQ(zeroCount, 1); // because wave is sin + + EXPECT_EQ(wave->getInt16Source().capacity(), inputSize); + EXPECT_EQ(wave->getInt16Source().size(), inputSize); + + std::vector int16Source = wave->getInt16Source(); + EXPECT_EQ(int16Source.capacity(), inputSize); + EXPECT_EQ(int16Source.size(), inputSize); + + zeroCount = 0; + for(short shortSrc : int16Source) { + if(shortSrc == 0) { + zeroCount++; + } + else + break; + } + EXPECT_EQ(zeroCount, 1); // because wave is sin + //int16_t data = wave->convertFloat2Int16(0.02); + //EXPECT_EQ(data, 0); +} + +INSTANTIATE_TEST_SUITE_P( + WaveTest, + WaveTestFixture, + ::testing::Values( + TestParamType(18000, 512), + TestParamType(18000, 1024), + TestParamType(18000, 2048), + TestParamType(20000, 2048), + TestParamType(21000, 2048) +)); \ No newline at end of file From c41eac6ab4b1d68618e8592dffd1aafd4c397ce5 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 06:55:35 +0900 Subject: [PATCH 07/31] Added `HexVector` the basic unit of acoustic data processing > Simply we can get the Heximal data's array. --- euphony/src/main/cpp/CMakeLists.txt | 1 + euphony/src/main/cpp/core/HexVector.h | 34 +++++++ .../src/main/cpp/core/source/HexVector.cpp | 91 +++++++++++++++++++ euphony/src/main/cpp/tests/CMakeLists.txt | 1 + euphony/src/main/cpp/tests/hexVectorTest.cpp | 51 +++++++++++ 5 files changed, 178 insertions(+) create mode 100644 euphony/src/main/cpp/core/HexVector.h create mode 100644 euphony/src/main/cpp/core/source/HexVector.cpp create mode 100644 euphony/src/main/cpp/tests/hexVectorTest.cpp diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index 9127ecb..d0aa807 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -24,6 +24,7 @@ target_link_libraries( set(EUPHONY_SRC arms/kiss_fft.c arms/kiss_fftr.c + core/source/HexVector.cpp core/source/Wave.cpp core/source/WaveBuilder.cpp ) diff --git a/euphony/src/main/cpp/core/HexVector.h b/euphony/src/main/cpp/core/HexVector.h new file mode 100644 index 0000000..6ae33ae --- /dev/null +++ b/euphony/src/main/cpp/core/HexVector.h @@ -0,0 +1,34 @@ +#ifndef EUPHONY_HEXVECTOR_H +#define EUPHONY_HEXVECTOR_H + +#include +#include + +namespace Euphony { + class HexVector { + public: + HexVector(int size); + HexVector(const HexVector& copy); + HexVector(const std::string& hexString); + HexVector(const std::vector& hexVectorCopy); + ~HexVector(); + + void pushBack(u_int8_t hexByte); + void popBack(); + std::string toString() const; + const std::vector &getHexSource() const; + void setHexSource(const std::vector &hexSource); + int getSize() const; + void clear(); + + std::__wrap_iter>::const_pointer> + begin() const; + std::__wrap_iter>::const_pointer> + end() const; + + private: + std::vector hexSource; + }; +} + +#endif //EUPHONY_HEXVECTOR_H diff --git a/euphony/src/main/cpp/core/source/HexVector.cpp b/euphony/src/main/cpp/core/source/HexVector.cpp new file mode 100644 index 0000000..1c98722 --- /dev/null +++ b/euphony/src/main/cpp/core/source/HexVector.cpp @@ -0,0 +1,91 @@ +#include "../HexVector.h" +#include +#include + +using namespace Euphony; + +HexVector::HexVector(int size) { + hexSource.reserve(size); +} + +HexVector::HexVector(const HexVector ©) { + setHexSource(copy.getHexSource()); +} + +HexVector::HexVector(const std::string& hexString) { + for(char c : hexString) { + switch(c) { + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': default: + hexSource.push_back(c - '0'); + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + hexSource.push_back(10 + c - 'a'); + break; + } + } +} + +HexVector::HexVector(const std::vector &hexVectorCopy) { + setHexSource(hexVectorCopy); +} + + +HexVector::~HexVector() { + hexSource.clear(); +} + +void HexVector::pushBack(u_int8_t hexByte) { + if(hexByte <= 0x0f) { + hexSource.push_back(hexByte); + } else { + hexSource.push_back((hexByte >> 4)); + hexSource.push_back(hexByte & 0x0f); + } +} + +void HexVector::popBack() { + hexSource.pop_back(); +} + +std::string HexVector::toString() const{ + std::stringstream result; + + for(auto data : hexSource) { + result << std::hex << (int) data; + } + + return result.str(); +} + +const std::vector &HexVector::getHexSource() const { + return hexSource; +} + +void HexVector::setHexSource(const std::vector &hexSrc) { + this->clear(); + for(u_int8_t hex : hexSrc) { + this->pushBack(hex); + } +} + +int HexVector::getSize() const { + return hexSource.size(); +} + +std::__wrap_iter>::const_pointer> +HexVector::begin() const{ + return hexSource.begin(); +} + +std::__wrap_iter>::const_pointer> +HexVector::end() const{ + return hexSource.end(); +} + +void HexVector::clear(){ + hexSource.clear(); +} diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index e1c922b..65c7ced 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -10,6 +10,7 @@ target_include_directories (gtest PUBLIC ${GOOGLETEST_ROOT}/include) add_executable( ${TEST_EUPHONY} + hexVectorTest.cpp waveTest.cpp waveBuilderTest.cpp ) diff --git a/euphony/src/main/cpp/tests/hexVectorTest.cpp b/euphony/src/main/cpp/tests/hexVectorTest.cpp new file mode 100644 index 0000000..ac73ec9 --- /dev/null +++ b/euphony/src/main/cpp/tests/hexVectorTest.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple, std::string> TestParamType; + +class HexVectorTestFixture : public ::testing::TestWithParam { + +public: + void createHexVector() { + EXPECT_EQ(hexVector, nullptr); + hexVector = new HexVector(0); + ASSERT_NE(hexVector, nullptr); + } + + HexVector* hexVector = nullptr; +}; + +TEST_P(HexVectorTestFixture, DefaultHex2StringTest) +{ + createHexVector(); + + std::vector source; + std::string expectedToStringResult; + + std::tie(source, expectedToStringResult) = GetParam(); + hexVector->setHexSource(source); + + EXPECT_EQ(hexVector->toString(), expectedToStringResult); +} + +INSTANTIATE_TEST_SUITE_P( + hexVectorTest, + HexVectorTestFixture, + ::testing::Values( + TestParamType(std::vector { 0 }, "0"), + TestParamType(std::vector { 1 }, "1"), + TestParamType(std::vector { 2 }, "2"), + TestParamType(std::vector { 0, 1 }, "01"), + TestParamType(std::vector { 0x01 }, "1"), + TestParamType(std::vector { 0x01, 0x0f }, "1f"), + TestParamType(std::vector { 1, 2 }, "12"), + TestParamType(std::vector { 1, 3, 5 }, "135"), + TestParamType(std::vector { 1, 0xa, 0xf }, "1af"), + TestParamType(std::vector { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }, "abcdef"), + TestParamType(std::vector { 0x1f, 0x2f, 0x3f, 0x4f }, "1f2f3f4f"), + TestParamType(std::vector { 0xff, 0xff, 0xff, 0x4f }, "ffffff4f") + ) +); \ No newline at end of file From 0d34663a2b04a04813b0eef277441fe511c554b7 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 07:07:14 +0900 Subject: [PATCH 08/31] Added Base Family (`Base2`, `Base16`, `BaseFactory` & `Base16Exception`) > Base2 : Binary data to process acoustic data > Base16 : heximal data to process acoustic data > BaseFactory : To create Base family series > Base16Exception : Exception for Base16 --- euphony/src/main/cpp/CMakeLists.txt | 3 ++ euphony/src/main/cpp/core/Base.h | 19 +++++++ euphony/src/main/cpp/core/Base16.h | 23 +++++++++ euphony/src/main/cpp/core/Base16Exception.h | 27 ++++++++++ euphony/src/main/cpp/core/Base2.h | 24 +++++++++ euphony/src/main/cpp/core/BaseFactory.h | 27 ++++++++++ euphony/src/main/cpp/core/source/Base16.cpp | 41 ++++++++++++++++ .../main/cpp/core/source/Base16Exception.cpp | 29 +++++++++++ euphony/src/main/cpp/core/source/Base2.cpp | 41 ++++++++++++++++ euphony/src/main/cpp/tests/CMakeLists.txt | 2 + euphony/src/main/cpp/tests/base16Test.cpp | 49 +++++++++++++++++++ euphony/src/main/cpp/tests/base2Test.cpp | 36 ++++++++++++++ 12 files changed, 321 insertions(+) create mode 100644 euphony/src/main/cpp/core/Base.h create mode 100644 euphony/src/main/cpp/core/Base16.h create mode 100644 euphony/src/main/cpp/core/Base16Exception.h create mode 100644 euphony/src/main/cpp/core/Base2.h create mode 100644 euphony/src/main/cpp/core/BaseFactory.h create mode 100644 euphony/src/main/cpp/core/source/Base16.cpp create mode 100644 euphony/src/main/cpp/core/source/Base16Exception.cpp create mode 100644 euphony/src/main/cpp/core/source/Base2.cpp create mode 100644 euphony/src/main/cpp/tests/base16Test.cpp create mode 100644 euphony/src/main/cpp/tests/base2Test.cpp diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index d0aa807..fa069d0 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -24,6 +24,9 @@ target_link_libraries( set(EUPHONY_SRC arms/kiss_fft.c arms/kiss_fftr.c + core/source/Base2.cpp + core/source/Base16.cpp + core/source/Base16Exception.cpp core/source/HexVector.cpp core/source/Wave.cpp core/source/WaveBuilder.cpp diff --git a/euphony/src/main/cpp/core/Base.h b/euphony/src/main/cpp/core/Base.h new file mode 100644 index 0000000..c89dd2b --- /dev/null +++ b/euphony/src/main/cpp/core/Base.h @@ -0,0 +1,19 @@ +#ifndef EUPHONY_BASE_H +#define EUPHONY_BASE_H + +#include +#include "HexVector.h" + +namespace Euphony { + class Base { + public: + virtual ~Base() = default; + + virtual std::string getBaseString() = 0; + virtual const HexVector &getHexVector() const = 0; + virtual int convertChar2Int(char src) const = 0; + virtual char convertInt2Char(int src) const = 0; + }; +} + +#endif //EUPHONY_BASE_H diff --git a/euphony/src/main/cpp/core/Base16.h b/euphony/src/main/cpp/core/Base16.h new file mode 100644 index 0000000..bbc83de --- /dev/null +++ b/euphony/src/main/cpp/core/Base16.h @@ -0,0 +1,23 @@ +#ifndef EUPHONY_BASE16_H +#define EUPHONY_BASE16_H + +#include "Base.h" +#include "Base16Exception.h" + +namespace Euphony { + + class Base16 : public Base { + public: + Base16(const HexVector &hexVectorSrc); + ~Base16() = default; + std::string getBaseString(); + const HexVector &getHexVector() const; + int convertChar2Int(char source) const; + char convertInt2Char(int source) const; + + private: + HexVector hexVector; + }; +} + +#endif //EUPHONY_BASE16_H diff --git a/euphony/src/main/cpp/core/Base16Exception.h b/euphony/src/main/cpp/core/Base16Exception.h new file mode 100644 index 0000000..7ed43c0 --- /dev/null +++ b/euphony/src/main/cpp/core/Base16Exception.h @@ -0,0 +1,27 @@ +#ifndef EUPHONY_BASE16EXCEPTION_H +#define EUPHONY_BASE16EXCEPTION_H + +#include +#include + +namespace Euphony { + + class Base16Exception : public std::exception { + public: + Base16Exception(); + Base16Exception(int line, const std::string & file ); + + ~Base16Exception() throw(); + + const char *what() const throw(); + const std::string & MSG() const; + + int LINE() const; + const std::string & FILE() const; + + private: + std::string msg, file; + int line; + }; +} +#endif //EUPHONY_BASE16EXCEPTION_H diff --git a/euphony/src/main/cpp/core/Base2.h b/euphony/src/main/cpp/core/Base2.h new file mode 100644 index 0000000..ced3014 --- /dev/null +++ b/euphony/src/main/cpp/core/Base2.h @@ -0,0 +1,24 @@ +#ifndef EUPHONY_BASE2_H +#define EUPHONY_BASE2_H + +#include "Base.h" + +namespace Euphony { + + class Base2 : public Base { + public: + Base2(const HexVector &hexVectorSrc); + ~Base2() = default; + std::string getBaseString(); + const HexVector &getHexVector() const; + int convertChar2Int(char source) const; + char convertInt2Char(int source) const; + + private: + HexVector hexVector; + + std::string hexToBase2(u_int8_t hex); + }; +} + +#endif //EUPHONY_BASE2_H diff --git a/euphony/src/main/cpp/core/BaseFactory.h b/euphony/src/main/cpp/core/BaseFactory.h new file mode 100644 index 0000000..7c32500 --- /dev/null +++ b/euphony/src/main/cpp/core/BaseFactory.h @@ -0,0 +1,27 @@ +#ifndef EUPHONY_BASEFACTORY_H +#define EUPHONY_BASEFACTORY_H + +#include "Definitions.h" +#include "Base2.h" +#include "Base16.h" + + +namespace Euphony { + class BaseFactory { + public: + static std::shared_ptr create(BaseType type, const HexVector &hexVectorSrc) { + switch(type) { + case BaseType::BASE2: + return std::make_shared(hexVectorSrc); + case BaseType::BASE16: + default: + return std::make_shared(hexVectorSrc); + } + } + }; +} + + + + +#endif //EUPHONY_BASEFACTORY_H diff --git a/euphony/src/main/cpp/core/source/Base16.cpp b/euphony/src/main/cpp/core/source/Base16.cpp new file mode 100644 index 0000000..3866886 --- /dev/null +++ b/euphony/src/main/cpp/core/source/Base16.cpp @@ -0,0 +1,41 @@ +#include "../Base16.h" +#include + +using namespace Euphony; + +Base16::Base16(const HexVector &hexVectorSrc) +: hexVector(hexVectorSrc) { } + +std::string Base16::getBaseString() { + return hexVector.toString(); +} + +int Euphony::Base16::convertChar2Int(char source) const { + switch(source) { + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': + return source - '0'; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + return source - 'a' + 10; + default: + throw Base16Exception(); + } +} + +char Base16::convertInt2Char(int source) const { + const char hexArray[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f'}; + + return hexArray[source]; +} + +const Euphony::HexVector &Euphony::Base16::getHexVector() const { + return hexVector; +} + + + + diff --git a/euphony/src/main/cpp/core/source/Base16Exception.cpp b/euphony/src/main/cpp/core/source/Base16Exception.cpp new file mode 100644 index 0000000..784430e --- /dev/null +++ b/euphony/src/main/cpp/core/source/Base16Exception.cpp @@ -0,0 +1,29 @@ +#include "../Definitions.h" +#include "../Base16Exception.h" + +using namespace Euphony; + +Base16Exception::Base16Exception() +: msg(BASE16_EXCEPTION_MSG), file(""), line(0) {} + +Base16Exception::Base16Exception(int _line, const std::string &_file) +: msg(BASE16_EXCEPTION_MSG), file(_file), line(_line) {} + +Base16Exception::~Base16Exception() throw() {} + +const char* Base16Exception::what() const throw() { + return msg.c_str(); +} + +const std::string &Base16Exception::MSG() const { + return msg; +} + +int Base16Exception::LINE() const { + return line; +} + +const std::string &Base16Exception::FILE() const { + return file; +} + diff --git a/euphony/src/main/cpp/core/source/Base2.cpp b/euphony/src/main/cpp/core/source/Base2.cpp new file mode 100644 index 0000000..00b23dd --- /dev/null +++ b/euphony/src/main/cpp/core/source/Base2.cpp @@ -0,0 +1,41 @@ +#include "../Base2.h" +#include + +using namespace Euphony; + +Base2::Base2(const HexVector &hexVectorSrc) +: hexVector(hexVectorSrc) { } + +std::string Base2::getBaseString() { + std::stringstream ss; + + for(u_int8_t hex : hexVector) { + ss << hexToBase2(hex); + } + + return ss.str(); +} + +const HexVector &Base2::getHexVector() const { + return hexVector; +} + +int Base2::convertChar2Int(char source) const { + return 0; +} + +char Base2::convertInt2Char(int source) const { + return 0; +} + +std::string Base2::hexToBase2(u_int8_t hex) { + std::string result; + + result.push_back(((hex & 0x8) >> 3) ? '1' : '0'); + result.push_back(((hex & 0x4) >> 2) ? '1' : '0'); + result.push_back(((hex & 0x2) >> 1) ? '1' : '0'); + result.push_back((hex & 0x1) ? '1' : '0'); + + return result; +} + diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index 65c7ced..8d73349 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -10,6 +10,8 @@ target_include_directories (gtest PUBLIC ${GOOGLETEST_ROOT}/include) add_executable( ${TEST_EUPHONY} + base2Test.cpp + base16Test.cpp hexVectorTest.cpp waveTest.cpp waveBuilderTest.cpp diff --git a/euphony/src/main/cpp/tests/base16Test.cpp b/euphony/src/main/cpp/tests/base16Test.cpp new file mode 100644 index 0000000..8bd8c7f --- /dev/null +++ b/euphony/src/main/cpp/tests/base16Test.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple, std::string> TestParamType; + +class Base16TranslationFixture : public ::testing::TestWithParam { + +public: + Base* base16 = nullptr; +}; + +TEST_P(Base16TranslationFixture, DefaultEncodingTest) +{ + std::vector source; + std::string expectedEncodedResult; + + std::tie(source, expectedEncodedResult) = GetParam(); + HexVector hv = HexVector(source); + base16 = new Base16(hv); + std::string actualResult = base16->getBaseString(); + EXPECT_EQ(actualResult, expectedEncodedResult); +} + +INSTANTIATE_TEST_SUITE_P( + Base16TranslationTest, + Base16TranslationFixture, + ::testing::Values( + TestParamType(std::vector{ 0x61 }, "61"), + TestParamType(std::vector{ 0x62 }, "62"), + TestParamType(std::vector{ 0x63 }, "63"), + TestParamType(std::vector{ 0x61, 0x62, 0x63 }, "616263"), + TestParamType(std::vector{ 0x6c, 0x6d, 0x6e, 0x6f }, "6c6d6e6f"), + TestParamType(std::vector{ 0x65, 0x66, 0x67 }, "656667"), + TestParamType(std::vector{ + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a + }, "6162636465666768696a6b6c6d6e6f707172737475767778797a"), + TestParamType(std::vector{ 0x41, 0x42, 0x43 }, "414243"), + TestParamType(std::vector{ + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a + }, "4142434445464748494a4b4c4d4e4f505152535455565758595a") + )); \ No newline at end of file diff --git a/euphony/src/main/cpp/tests/base2Test.cpp b/euphony/src/main/cpp/tests/base2Test.cpp new file mode 100644 index 0000000..eec9151 --- /dev/null +++ b/euphony/src/main/cpp/tests/base2Test.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple, std::string> TestParamType; + +class Base2TranslationFixture : public ::testing::TestWithParam { +public: + Base* base2 = nullptr; +}; + +TEST_P(Base2TranslationFixture, DefaultBase2Test) +{ + std::vector source; + std::string expectedEncodedResult; + + std::tie(source, expectedEncodedResult) = GetParam(); + HexVector hv = HexVector(source); + base2 = new Base2(hv); + std::string actualResult = base2->getBaseString(); + EXPECT_EQ(actualResult, expectedEncodedResult); +} + +INSTANTIATE_TEST_SUITE_P( + Base2TranslationTest, + Base2TranslationFixture, + ::testing::Values( + TestParamType(std::vector{ 0x61 }, "01100001"), + TestParamType(std::vector{ 0x62 }, "01100010"), + TestParamType(std::vector{ 0x63 }, "01100011"), + TestParamType(std::vector{ 0x61, 0x62, 0x63 }, "011000010110001001100011"), + TestParamType(std::vector{ 0x78, 0x79, 0x7a }, "011110000111100101111010") +)); \ No newline at end of file From 390aa284b27dc2b20e84fd1e2e07be1dd79f6ff5 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 07:34:12 +0900 Subject: [PATCH 09/31] Added `Charset` interface to interpret characters > Implement `ASCIICharset` > Implement `DefaultCharset` (Fake) > All unit-test were passed. --- euphony/src/main/cpp/CMakeLists.txt | 2 + euphony/src/main/cpp/core/ASCIICharset.h | 17 +++++ euphony/src/main/cpp/core/Charset.h | 16 +++++ euphony/src/main/cpp/core/DefaultCharset.h | 18 ++++++ .../src/main/cpp/core/source/ASCIICharset.cpp | 27 ++++++++ .../main/cpp/core/source/DefaultCharset.cpp | 10 +++ euphony/src/main/cpp/tests/CMakeLists.txt | 2 + .../src/main/cpp/tests/asciiCharsetTest.cpp | 64 +++++++++++++++++++ .../src/main/cpp/tests/defaultCharsetTest.cpp | 47 ++++++++++++++ 9 files changed, 203 insertions(+) create mode 100644 euphony/src/main/cpp/core/ASCIICharset.h create mode 100644 euphony/src/main/cpp/core/Charset.h create mode 100644 euphony/src/main/cpp/core/DefaultCharset.h create mode 100644 euphony/src/main/cpp/core/source/ASCIICharset.cpp create mode 100644 euphony/src/main/cpp/core/source/DefaultCharset.cpp create mode 100644 euphony/src/main/cpp/tests/asciiCharsetTest.cpp create mode 100644 euphony/src/main/cpp/tests/defaultCharsetTest.cpp diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index fa069d0..db40095 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -24,9 +24,11 @@ target_link_libraries( set(EUPHONY_SRC arms/kiss_fft.c arms/kiss_fftr.c + core/source/ASCIICharset.cpp core/source/Base2.cpp core/source/Base16.cpp core/source/Base16Exception.cpp + core/source/DefaultCharset.cpp core/source/HexVector.cpp core/source/Wave.cpp core/source/WaveBuilder.cpp diff --git a/euphony/src/main/cpp/core/ASCIICharset.h b/euphony/src/main/cpp/core/ASCIICharset.h new file mode 100644 index 0000000..13f9ff7 --- /dev/null +++ b/euphony/src/main/cpp/core/ASCIICharset.h @@ -0,0 +1,17 @@ +#ifndef EUPHONY_ASCIICHARSET_H +#define EUPHONY_ASCIICHARSET_H + +#include "Charset.h" + +namespace Euphony { + + class ASCIICharset : public Charset { + public: + ASCIICharset() = default; + ~ASCIICharset() = default; + HexVector encode(std::string src); + std::string decode(const HexVector &src); + }; +} + +#endif //EUPHONY_ASCIICHARSET_H diff --git a/euphony/src/main/cpp/core/Charset.h b/euphony/src/main/cpp/core/Charset.h new file mode 100644 index 0000000..5e932f9 --- /dev/null +++ b/euphony/src/main/cpp/core/Charset.h @@ -0,0 +1,16 @@ +#ifndef EUPHONY_CHARSET_H +#define EUPHONY_CHARSET_H + +#include +#include "HexVector.h" + +namespace Euphony { + class Charset { + public: + virtual ~Charset() = default; + + virtual HexVector encode(std::string src) = 0; + virtual std::string decode(const HexVector &src) = 0; + }; +} +#endif //EUPHONY_CHARSET_H diff --git a/euphony/src/main/cpp/core/DefaultCharset.h b/euphony/src/main/cpp/core/DefaultCharset.h new file mode 100644 index 0000000..e89b311 --- /dev/null +++ b/euphony/src/main/cpp/core/DefaultCharset.h @@ -0,0 +1,18 @@ +#ifndef EUPHONY_DEFAULTCHARSET_H +#define EUPHONY_DEFAULTCHARSET_H + +#include "Charset.h" + +namespace Euphony { + + class DefaultCharset : public Charset { + public: + DefaultCharset() = default; + ~DefaultCharset() = default; + HexVector encode(std::string src); + std::string decode(const HexVector &src); + }; +} + + +#endif //EUPHONY_DEFAULTCHARSET_H diff --git a/euphony/src/main/cpp/core/source/ASCIICharset.cpp b/euphony/src/main/cpp/core/source/ASCIICharset.cpp new file mode 100644 index 0000000..98d8b37 --- /dev/null +++ b/euphony/src/main/cpp/core/source/ASCIICharset.cpp @@ -0,0 +1,27 @@ +#include "../ASCIICharset.h" +#include +#include + +using namespace Euphony; + +HexVector ASCIICharset::encode(std::string src) { + HexVector result(src.size()); + + for(char &c : src) { + result.pushBack(c); + } + + return result; +} + +std::string ASCIICharset::decode(const HexVector &src) { + std::string result; + + std::string stringSrc = src.toString(); + for(int i = 0; i < stringSrc.size() - 1; i+=2) { + std::string c = stringSrc.substr(i, 2); + result.push_back((char) (int) strtol(c.c_str(), nullptr, 16)); + } + + return result; +} diff --git a/euphony/src/main/cpp/core/source/DefaultCharset.cpp b/euphony/src/main/cpp/core/source/DefaultCharset.cpp new file mode 100644 index 0000000..d6c9213 --- /dev/null +++ b/euphony/src/main/cpp/core/source/DefaultCharset.cpp @@ -0,0 +1,10 @@ +#include "../DefaultCharset.h" +using namespace Euphony; + +HexVector DefaultCharset::encode(std::string src) { + return HexVector(src); +} + +std::string DefaultCharset::decode(const HexVector& src) { + return src.toString(); +} diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index 8d73349..b3ab8bf 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -10,8 +10,10 @@ target_include_directories (gtest PUBLIC ${GOOGLETEST_ROOT}/include) add_executable( ${TEST_EUPHONY} + asciiCharsetTest.cpp base2Test.cpp base16Test.cpp + defaultCharsetTest.cpp hexVectorTest.cpp waveTest.cpp waveBuilderTest.cpp diff --git a/euphony/src/main/cpp/tests/asciiCharsetTest.cpp b/euphony/src/main/cpp/tests/asciiCharsetTest.cpp new file mode 100644 index 0000000..420689b --- /dev/null +++ b/euphony/src/main/cpp/tests/asciiCharsetTest.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class ASCIICharsetTestFixture : public ::testing::TestWithParam { + +public: + void openCharset() { + EXPECT_EQ(charset, nullptr); + charset = new ASCIICharset(); + ASSERT_NE(charset, nullptr); + } + + Charset* charset = nullptr; +}; + + +TEST_P(ASCIICharsetTestFixture, EncodingTest) +{ + openCharset(); + + std::string source; + std::string expectedResult; + + std::tie(source, expectedResult) = GetParam(); + + HexVector actualResult = charset->encode(source); + EXPECT_EQ(actualResult.toString(), expectedResult); +} + + +TEST_P(ASCIICharsetTestFixture, DecodingTest) +{ + openCharset(); + + std::string source; + std::string expectedResult; + + std::tie(expectedResult, source) = GetParam(); + HexVector hv = HexVector(source); + + std::string actualResult = charset->decode(hv); + EXPECT_EQ(actualResult, expectedResult); +} + +INSTANTIATE_TEST_CASE_P( + ChrasetDecodingTestSuite, + ASCIICharsetTestFixture, + ::testing::Values( + TestParamType("a", "61"), + TestParamType("b", "62"), + TestParamType("c", "63"), + TestParamType("abc", "616263"), + TestParamType("lmno", "6c6d6e6f"), + TestParamType("efg", "656667"), + TestParamType("abcdefghijklmnopqrstuvwxyz", "6162636465666768696a6b6c6d6e6f707172737475767778797a"), + TestParamType("ABC", "414243"), + TestParamType("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "4142434445464748494a4b4c4d4e4f505152535455565758595a") + )); \ No newline at end of file diff --git a/euphony/src/main/cpp/tests/defaultCharsetTest.cpp b/euphony/src/main/cpp/tests/defaultCharsetTest.cpp new file mode 100644 index 0000000..34d5a3f --- /dev/null +++ b/euphony/src/main/cpp/tests/defaultCharsetTest.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class DefaultCharsetTestFixture : public ::testing::TestWithParam { + +public: + void openCharset() { + EXPECT_EQ(charset, nullptr); + charset = new DefaultCharset(); + ASSERT_NE(charset, nullptr); + } + + Charset* charset = nullptr; +}; + +TEST_P(DefaultCharsetTestFixture, DecodingTest) +{ + openCharset(); + + std::string source; + + std::tie(source) = GetParam(); + + std::string actualResult = charset->decode(source); + EXPECT_EQ(actualResult, source); +} + +INSTANTIATE_TEST_CASE_P( + ChrasetDecodingTestSuite, + DefaultCharsetTestFixture, + ::testing::Values( + TestParamType("61"), + TestParamType("62"), + TestParamType("63"), + TestParamType("616263"), + TestParamType("6c6d6e6f"), + TestParamType("656667"), + TestParamType("6162636465666768696a6b6c6d6e6f707172737475767778797a"), + TestParamType("414243"), + TestParamType("4142434445464748494a4b4c4d4e4f505152535455565758595a") +)); \ No newline at end of file From c1b6bf8dfe7a1406bef4727673033c5257b92f3d Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 07:39:40 +0900 Subject: [PATCH 10/31] Added `PacketErrorDetector` (includes Parity & Checksum) > Unit-test was passed (Parity, Checksum) --- euphony/src/main/cpp/CMakeLists.txt | 1 + .../src/main/cpp/core/PacketErrorDetector.h | 20 ++++ .../cpp/core/source/PacketErrorDetector.cpp | 91 +++++++++++++++++++ euphony/src/main/cpp/tests/CMakeLists.txt | 1 + .../cpp/tests/packetErrorDetectorTest.cpp | 78 ++++++++++++++++ 5 files changed, 191 insertions(+) create mode 100644 euphony/src/main/cpp/core/PacketErrorDetector.h create mode 100644 euphony/src/main/cpp/core/source/PacketErrorDetector.cpp create mode 100644 euphony/src/main/cpp/tests/packetErrorDetectorTest.cpp diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index db40095..3c6333a 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -30,6 +30,7 @@ set(EUPHONY_SRC core/source/Base16Exception.cpp core/source/DefaultCharset.cpp core/source/HexVector.cpp + core/source/PacketErrorDetector.cpp core/source/Wave.cpp core/source/WaveBuilder.cpp ) diff --git a/euphony/src/main/cpp/core/PacketErrorDetector.h b/euphony/src/main/cpp/core/PacketErrorDetector.h new file mode 100644 index 0000000..b4ef1ea --- /dev/null +++ b/euphony/src/main/cpp/core/PacketErrorDetector.h @@ -0,0 +1,20 @@ +#ifndef EUPHONY_PACKETERRORDETECTOR_H +#define EUPHONY_PACKETERRORDETECTOR_H + +#include +#include +#include "HexVector.h" + +namespace Euphony { + + class PacketErrorDetector { + public: + static std::string makeParityAndChecksum(const HexVector& payload); + static std::string makeParityAndChecksum(std::string payloadStr); + static HexVector makeChecksum(const HexVector& payload); + static HexVector makeParallelParity(const HexVector& payload); + static bool verifyChecksum(const HexVector& payload, int checksum); + static bool verifyParallelParity(const HexVector& payload, int parity); + }; +} +#endif //EUPHONY_PACKETERRORDETECTOR_H diff --git a/euphony/src/main/cpp/core/source/PacketErrorDetector.cpp b/euphony/src/main/cpp/core/source/PacketErrorDetector.cpp new file mode 100644 index 0000000..9b07877 --- /dev/null +++ b/euphony/src/main/cpp/core/source/PacketErrorDetector.cpp @@ -0,0 +1,91 @@ +#include "../PacketErrorDetector.h" +#include + +using namespace Euphony; + +std::string PacketErrorDetector::makeParityAndChecksum(const HexVector& payload) { + char hexArray[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f'}; + + int evenParity[4] = {0,}; + int payloadSum = 0; + + for(int v : payload) { + evenParity[0] += ((0x8 & v) >> 3); + evenParity[1] += ((0x4 & v) >> 2); + evenParity[2] += ((0x2 & v) >> 1); + evenParity[3] += (0x1 & v); + payloadSum += v; + } + + payloadSum &= 0xF; + payloadSum = (~payloadSum + 1) & 0xF; + int evenParityResult = + (evenParity[0] & 0x1) * 8 + (evenParity[1] & 0x1) * 4 + + (evenParity[2] & 0x1) * 2 + (evenParity[3] & 0x1); + + std::stringstream result; + result << hexArray[payloadSum] << hexArray[evenParityResult]; + + return result.str(); +} + +std::string PacketErrorDetector::makeParityAndChecksum(std::string payload) { + HexVector hexVector = HexVector(payload); + return makeParityAndChecksum(hexVector); +} + +HexVector PacketErrorDetector::makeChecksum(const HexVector& payload) { + int payloadSum = 0; + + for(int v : payload) { + payloadSum += v; + } + + payloadSum &= 0xF; + payloadSum = (~payloadSum + 1) & 0xF; + + HexVector result(1); + result.pushBack(payloadSum); + + return result; +} + +HexVector PacketErrorDetector::makeParallelParity(const HexVector& payload) { + int evenParity[4] = {0,}; + + for(int v : payload) { + evenParity[0] += ((0x8 & v) >> 3); + evenParity[1] += ((0x4 & v) >> 2); + evenParity[2] += ((0x2 & v) >> 1); + evenParity[3] += (0x1 & v); + } + + int evenParityResult = + (evenParity[0] & 0x1) * 8 + (evenParity[1] & 0x1) * 4 + + (evenParity[2] & 0x1) * 2 + (evenParity[3] & 0x1); + + + HexVector result(1); + result.pushBack(evenParityResult); + + return result; +} + +bool PacketErrorDetector::verifyChecksum(const HexVector& payload, int checksum) { + HexVector checksumResult = makeChecksum(payload); + + if(checksumResult.getHexSource()[0] != checksum) + return false; + else + return true; +} + +bool PacketErrorDetector::verifyParallelParity(const HexVector& payload, int parity) { + HexVector parityResult = makeParallelParity(payload); + + if(parityResult.getHexSource()[0] != parity) + return false; + else + return true; +} diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index b3ab8bf..239b046 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -15,6 +15,7 @@ add_executable( base16Test.cpp defaultCharsetTest.cpp hexVectorTest.cpp + packetErrorDetectorTest.cpp waveTest.cpp waveBuilderTest.cpp ) diff --git a/euphony/src/main/cpp/tests/packetErrorDetectorTest.cpp b/euphony/src/main/cpp/tests/packetErrorDetectorTest.cpp new file mode 100644 index 0000000..1452a2e --- /dev/null +++ b/euphony/src/main/cpp/tests/packetErrorDetectorTest.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class PacketErrorDetectorTestFixture : public ::testing::TestWithParam { +}; + +TEST_P(PacketErrorDetectorTestFixture, PacketErrorDetectorTest) +{ + std::string source; + std::string expectedResult; + + std::tie(source, expectedResult) = GetParam(); + + std::string actualResult = PacketErrorDetector::makeParityAndChecksum(source); + EXPECT_EQ(expectedResult, actualResult); +} + +TEST_F(PacketErrorDetectorTestFixture, ErrorCodeTest) +{ + + std::vector source {0x6, 0x1}; + std::string expectedResult = "97"; + HexVector hv = HexVector(source); + EXPECT_EQ(expectedResult, PacketErrorDetector::makeParityAndChecksum(hv.getHexSource())); + + std::vector source2 {0x61, 0x62, 0x63}; + std::string expectedResult2 = "86"; + HexVector hv2 = HexVector(source2); + EXPECT_EQ(expectedResult2, PacketErrorDetector::makeParityAndChecksum(hv2.getHexSource())); +} + + +TEST_F(PacketErrorDetectorTestFixture, ChecksumTest) +{ + std::vector source {0x6, 0x8, 0x6, 0x5, 0x6, 0xc, 0x6, 0xc, 0x6, 0xf}; + + HexVector hv = HexVector(source); + HexVector actualResult = HexVector(1); + actualResult.pushBack(14); + EXPECT_EQ(PacketErrorDetector::makeChecksum(hv).toString(), actualResult.toString()); + EXPECT_EQ(PacketErrorDetector::verifyChecksum(hv, 14), true); + EXPECT_EQ(PacketErrorDetector::verifyChecksum(hv, 13), false); +} + +TEST_F(PacketErrorDetectorTestFixture, ParityCodeTest) +{ + std::vector source {0x6, 0x8, 0x6, 0x5, 0x6, 0xc, 0x6, 0xc, 0x6, 0xf}; + + HexVector hv = HexVector(source); + HexVector actualResult = HexVector(1); + actualResult.pushBack(4); + EXPECT_EQ(PacketErrorDetector::makeParallelParity(hv).toString(), actualResult.toString()); + EXPECT_EQ(PacketErrorDetector::verifyParallelParity(hv, 4), true); + EXPECT_EQ(PacketErrorDetector::verifyParallelParity(hv, 5), false); +} + +INSTANTIATE_TEST_CASE_P( + PacketErrorDetectorTestSuite, + PacketErrorDetectorTestFixture, + ::testing::Values( + TestParamType("61", "97"), + TestParamType("62", "84"), + TestParamType("63", "75"), + TestParamType("616263", "86"), + TestParamType("6c6d6e6f", "20"), + TestParamType("656667", "c2"), + TestParamType("6162636465666768696a6b6c6d6e6f707172737475767778797a", "aa"), + TestParamType("414243", "e4"), + TestParamType("4142434445464748494a4b4c4d4e4f505152535455565758595a", "ea") +)); From 9e6f94343adc6663970306644d9e9b501804c8d0 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 07:44:18 +0900 Subject: [PATCH 11/31] Added `Packet` concept (basic communication unit) > Applied PacketBuilder > Unit-test were passed! (Packet, PacketBuilder) --- euphony/src/main/cpp/CMakeLists.txt | 2 + euphony/src/main/cpp/core/Packet.h | 40 +++++++ euphony/src/main/cpp/core/PacketBuilder.h | 19 +++ euphony/src/main/cpp/core/source/Packet.cpp | 108 ++++++++++++++++++ .../main/cpp/core/source/PacketBuilder.cpp | 30 +++++ euphony/src/main/cpp/tests/CMakeLists.txt | 2 + .../src/main/cpp/tests/packetBuilderTest.cpp | 69 +++++++++++ euphony/src/main/cpp/tests/packetTest.cpp | 61 ++++++++++ 8 files changed, 331 insertions(+) create mode 100644 euphony/src/main/cpp/core/Packet.h create mode 100644 euphony/src/main/cpp/core/PacketBuilder.h create mode 100644 euphony/src/main/cpp/core/source/Packet.cpp create mode 100644 euphony/src/main/cpp/core/source/PacketBuilder.cpp create mode 100644 euphony/src/main/cpp/tests/packetBuilderTest.cpp create mode 100644 euphony/src/main/cpp/tests/packetTest.cpp diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index 3c6333a..0f654e0 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -30,6 +30,8 @@ set(EUPHONY_SRC core/source/Base16Exception.cpp core/source/DefaultCharset.cpp core/source/HexVector.cpp + core/source/Packet.cpp + core/source/PacketBuilder.cpp core/source/PacketErrorDetector.cpp core/source/Wave.cpp core/source/WaveBuilder.cpp diff --git a/euphony/src/main/cpp/core/Packet.h b/euphony/src/main/cpp/core/Packet.h new file mode 100644 index 0000000..7fe26e7 --- /dev/null +++ b/euphony/src/main/cpp/core/Packet.h @@ -0,0 +1,40 @@ +#ifndef EUPHONY_PACKET_H +#define EUPHONY_PACKET_H + +#include "Definitions.h" +#include "Base.h" +#include + +namespace Euphony { + class PacketBuilder; + + class Packet { + public: + Packet(); + Packet(const HexVector& source); + Packet(const BaseType type, const HexVector& source); + static PacketBuilder create(); + void clear(); + + std::shared_ptr getChecksum(); + std::shared_ptr getParityCode(); + std::string getPayloadStr() const; + void setPayload(std::shared_ptr payloadSrc); + void setPayload(const HexVector& source); + BaseType getBaseType() const; + void setBaseType(BaseType baseTypeSrc); + + std::string toString(); + + private: + friend class PacketBuilder; + BaseType baseType; + std::shared_ptr payload; + std::shared_ptr checksum; + std::shared_ptr parityCode; + bool isVerified; + void initialize(); + }; +} + +#endif //EUPHONY_PACKET_H diff --git a/euphony/src/main/cpp/core/PacketBuilder.h b/euphony/src/main/cpp/core/PacketBuilder.h new file mode 100644 index 0000000..954e89c --- /dev/null +++ b/euphony/src/main/cpp/core/PacketBuilder.h @@ -0,0 +1,19 @@ +#ifndef EUPHONY_PACKETBUILDER_H +#define EUPHONY_PACKETBUILDER_H + +#include "Packet.h" + +namespace Euphony { + class PacketBuilder { + public: + PacketBuilder& setPayload(const HexVector& source); + PacketBuilder& setPayloadWithASCII(std::string asciiSrc); + PacketBuilder& basedOnBase16(); + PacketBuilder& basedOnBase2(); + std::shared_ptr build(); + + private: + Packet packet; + }; +} +#endif //EUPHONY_PACKETBUILDER_H diff --git a/euphony/src/main/cpp/core/source/Packet.cpp b/euphony/src/main/cpp/core/source/Packet.cpp new file mode 100644 index 0000000..397a158 --- /dev/null +++ b/euphony/src/main/cpp/core/source/Packet.cpp @@ -0,0 +1,108 @@ +#include "../Packet.h" +#include "../PacketBuilder.h" +#include "../PacketErrorDetector.h" +#include "../BaseFactory.h" +#include +using namespace Euphony; + + +Packet::Packet() + : baseType(BaseType::BASE16), + payload(nullptr), + checksum(nullptr), + parityCode(nullptr), + isVerified(false) { +} + +Packet::Packet(const HexVector& source) + : baseType(BaseType::BASE16), + payload(nullptr), + checksum(nullptr), + parityCode(nullptr), + isVerified(false) { + payload = BaseFactory::create(baseType, source); + initialize(); +} + +Packet::Packet(const BaseType baseTypeSrc, const HexVector& source) +: baseType(baseTypeSrc), + payload(nullptr), + checksum(nullptr), + parityCode(nullptr), + isVerified(false) { + payload = BaseFactory::create(baseType, source); + initialize(); +} + + +PacketBuilder Packet::create() { + return PacketBuilder(); +} + +void Packet::initialize() { + //std::string errorCode = PacketErrorDetector::makeParityAndChecksum(payload->getBaseString()); + //this->checksum = BaseFactory::create(baseType, ) + this->checksum = BaseFactory::create( + baseType, + PacketErrorDetector::makeChecksum(payload->getHexVector()) + ); + this->parityCode = BaseFactory::create( + baseType, + PacketErrorDetector::makeParallelParity(payload->getHexVector()) + ); + + this->isVerified = true; +} + +void Packet::clear() { + this->checksum = nullptr; + this->parityCode = nullptr; + this->isVerified = false; + + payload = nullptr; +} + +std::string Packet::getPayloadStr() const { + return payload->getBaseString(); +} + +std::string Packet::toString() { + std::stringstream result; + + result << "S" << + payload->getBaseString() << + checksum->getBaseString() << + parityCode->getBaseString(); + + return result.str(); +} + +void Packet::setPayload(std::shared_ptr payloadSrc) { + payload = std::move(payloadSrc); + initialize(); +} + +void Packet::setPayload(const HexVector &source) { + payload = BaseFactory::create(baseType, source); + initialize(); +} + +std::shared_ptr Packet::getChecksum() { + return checksum; +} + +std::shared_ptr Packet::getParityCode() { + return parityCode; +} + +BaseType Packet::getBaseType() const { + return baseType; +} + +void Packet::setBaseType(BaseType baseTypeSrc) { + baseType = baseTypeSrc; + if(payload != nullptr) { + payload = BaseFactory::create(baseType, payload->getHexVector()); + initialize(); + } +} diff --git a/euphony/src/main/cpp/core/source/PacketBuilder.cpp b/euphony/src/main/cpp/core/source/PacketBuilder.cpp new file mode 100644 index 0000000..48d5d08 --- /dev/null +++ b/euphony/src/main/cpp/core/source/PacketBuilder.cpp @@ -0,0 +1,30 @@ +#include "../DefaultCharset.h" +#include "../ASCIICharset.h" +#include "../Definitions.h" +#include "../PacketBuilder.h" + +using namespace Euphony; + +PacketBuilder &Euphony::PacketBuilder::setPayload(const HexVector &source) { + packet.setPayload(source); + return *this; +} + +std::shared_ptr Euphony::PacketBuilder::build() { + return std::make_shared(packet); +} + +PacketBuilder &PacketBuilder::basedOnBase16() { + packet.setBaseType(BaseType::BASE16); + return *this; +} + +PacketBuilder &PacketBuilder::basedOnBase2() { + packet.setBaseType(BaseType::BASE2); + return *this; +} + +PacketBuilder &PacketBuilder::setPayloadWithASCII(std::string asciiSrc) { + packet.setPayload(HexVector(ASCIICharset().encode(asciiSrc))); + return *this; +} \ No newline at end of file diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index 239b046..5d32829 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -15,6 +15,8 @@ add_executable( base16Test.cpp defaultCharsetTest.cpp hexVectorTest.cpp + packetTest.cpp + packetBuilderTest.cpp packetErrorDetectorTest.cpp waveTest.cpp waveBuilderTest.cpp diff --git a/euphony/src/main/cpp/tests/packetBuilderTest.cpp b/euphony/src/main/cpp/tests/packetBuilderTest.cpp new file mode 100644 index 0000000..01a3e1e --- /dev/null +++ b/euphony/src/main/cpp/tests/packetBuilderTest.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class PacketBuilderTestFixture : public ::testing::TestWithParam { + +public: + std::shared_ptr pkt = nullptr; +}; + +TEST_P(PacketBuilderTestFixture, CreationForASCIIAndBase16TestByPacketBuilder) +{ + std::string source; + std::string expectedResult; + + std::tie(source, expectedResult) = GetParam(); + + pkt = Packet::create() + .setPayloadWithASCII(source) + .basedOnBase16() + .build(); + + // Check total result + EXPECT_EQ(pkt->toString(), expectedResult); +} + +TEST_F(PacketBuilderTestFixture, CreationForASCIIAndBase2TestByPacketBuilder) +{ + std::string source = "a"; + std::string expectedResult = "S0110000110010111"; + pkt = Packet::create() + .setPayloadWithASCII(source) + .basedOnBase2() + .build(); + // Check total result + EXPECT_EQ(pkt->toString(), expectedResult); + + pkt->clear(); + source = "abc"; + expectedResult = "S01100001011000100110001110000110"; + pkt = Packet::create() + .setPayloadWithASCII(source) + .basedOnBase2() + .build(); + + EXPECT_EQ(pkt->toString(), expectedResult); +} + +INSTANTIATE_TEST_SUITE_P( + PacketBuilderTest, + PacketBuilderTestFixture, + ::testing::Values( + TestParamType("a", "S6197"), + TestParamType("b", "S6284"), + TestParamType("c", "S6375"), + TestParamType("abc", "S61626386"), + TestParamType("lmno", "S6c6d6e6f20"), + TestParamType("efg", "S656667c2"), + TestParamType("abcdefghijklmnopqrstuvwxyz", "S6162636465666768696a6b6c6d6e6f707172737475767778797aaa"), + TestParamType("ABC", "S414243e4"), + TestParamType("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "S4142434445464748494a4b4c4d4e4f505152535455565758595aea") + ) +); diff --git a/euphony/src/main/cpp/tests/packetTest.cpp b/euphony/src/main/cpp/tests/packetTest.cpp new file mode 100644 index 0000000..ebbed84 --- /dev/null +++ b/euphony/src/main/cpp/tests/packetTest.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class PacketTestFixture : public ::testing::TestWithParam { + +public: + Packet* pkt = nullptr; +}; + +TEST_P(PacketTestFixture, PacketCreationForASCIIAndBase16Test) +{ + std::string source; + std::string expectedResult; + + std::tie(source, expectedResult) = GetParam(); + + HexVector hv = ASCIICharset().encode(source); + pkt = new Packet(hv); + + // Check total result + EXPECT_EQ(pkt->toString(), expectedResult); +} + +TEST_F(PacketTestFixture, PacketCreationForASCIIAndBase2Test) +{ + std::string source = "a"; + std::string expectedResult = "S0110000110010111"; + HexVector hv = ASCIICharset().encode(source); + pkt = new Packet(BaseType::BASE2, hv); + // Check total result + EXPECT_EQ(pkt->toString(), expectedResult); + + pkt->clear(); + source = "abc"; + expectedResult = "S01100001011000100110001110000110"; + hv = ASCIICharset().encode(source); + pkt->setPayload(hv); + EXPECT_EQ(pkt->toString(), expectedResult); +} + +INSTANTIATE_TEST_CASE_P( + PacketTestSuite, + PacketTestFixture, + ::testing::Values( + TestParamType("a", "S6197"), + TestParamType("b", "S6284"), + TestParamType("c", "S6375"), + TestParamType("abc", "S61626386"), + TestParamType("lmno", "S6c6d6e6f20"), + TestParamType("efg", "S656667c2"), + TestParamType("abcdefghijklmnopqrstuvwxyz", "S6162636465666768696a6b6c6d6e6f707172737475767778797aaa"), + TestParamType("ABC", "S414243e4"), + TestParamType("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "S4142434445464748494a4b4c4d4e4f505152535455565758595aea") + )); \ No newline at end of file From 47664d8603e104454599efefb449a440d7869501 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 07:56:44 +0900 Subject: [PATCH 12/31] Created FFTModel interface & Implement FFTProcessor based on FFTModel > Unit-test was not tested. --- euphony/src/main/cpp/CMakeLists.txt | 2 + euphony/src/main/cpp/core/FFTModel.h | 28 ++++++++++ euphony/src/main/cpp/core/FFTProcessor.h | 42 +++++++++++++++ euphony/src/main/cpp/core/source/FFTModel.cpp | 26 +++++++++ .../src/main/cpp/core/source/FFTProcessor.cpp | 54 +++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 euphony/src/main/cpp/core/FFTModel.h create mode 100644 euphony/src/main/cpp/core/FFTProcessor.h create mode 100644 euphony/src/main/cpp/core/source/FFTModel.cpp create mode 100644 euphony/src/main/cpp/core/source/FFTProcessor.cpp diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index 0f654e0..4689f8f 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -29,6 +29,8 @@ set(EUPHONY_SRC core/source/Base16.cpp core/source/Base16Exception.cpp core/source/DefaultCharset.cpp + core/source/FFTModel.cpp + core/source/FFTProcessor.cpp core/source/HexVector.cpp core/source/Packet.cpp core/source/PacketBuilder.cpp diff --git a/euphony/src/main/cpp/core/FFTModel.h b/euphony/src/main/cpp/core/FFTModel.h new file mode 100644 index 0000000..0e40e2b --- /dev/null +++ b/euphony/src/main/cpp/core/FFTModel.h @@ -0,0 +1,28 @@ +// +// Created by desig on 2020-11-15. +// + +#ifndef EUPHONY_FFTMODEL_H +#define EUPHONY_FFTMODEL_H + +namespace Euphony { + class FFTModel { + public: + FFTModel(int fftSizeSrc, int sampleRateSrc); + virtual ~FFTModel() = default; + + virtual float *makeSpectrum(short *pcmSrc) = 0; + + int getFFTSize() const; + void setFFTSize(int fftSize); + int getSampleRate() const; + void setSampleRate(int sampleRate); + + private: + int fftSize; + int sampleRate; + + }; +} + +#endif //EUPHONY_FFTMODEL_H diff --git a/euphony/src/main/cpp/core/FFTProcessor.h b/euphony/src/main/cpp/core/FFTProcessor.h new file mode 100644 index 0000000..a1d7f19 --- /dev/null +++ b/euphony/src/main/cpp/core/FFTProcessor.h @@ -0,0 +1,42 @@ +// +// Created by opener on 20. 11. 30. +// + +#ifndef EUPHONY_FFTPROCESSOR_H +#define EUPHONY_FFTPROCESSOR_H + +#include +#include "../arms/kiss_fftr.h" +#include "FFTModel.h" + +/* + * Define FFT Structure + * This space is similar to device driver's structure. + * Euphony is now using KissFFT Library. + * If you want to change FFT Library, Just fulfill FFTModel interface. + */ + +namespace Euphony { + + class FFTProcessor : public FFTModel { + public: + FFTProcessor(int fft_size, int sample_rate); + ~FFTProcessor(); + virtual float* makeSpectrum(short* src); + int getResultSize() const; + + private: + static inline float shortToFloat(const short val); + inline int frequencyToIndex(const int freq) const; + + kiss_fftr_cfg config; + /* unique_ptr's array version. it is available on c++14. */ + kiss_fft_cpx* spectrum; + float* result; + int fftSize; + int halfOfFFTSize; + }; +} + + +#endif //EUPHONY_FFTPROCESSOR_H diff --git a/euphony/src/main/cpp/core/source/FFTModel.cpp b/euphony/src/main/cpp/core/source/FFTModel.cpp new file mode 100644 index 0000000..89a8e89 --- /dev/null +++ b/euphony/src/main/cpp/core/source/FFTModel.cpp @@ -0,0 +1,26 @@ +#include "../FFTModel.h" +#include "../Definitions.h" + +using namespace Euphony; + +FFTModel::FFTModel(int fftSizeSrc, int sampleRateSrc) +: fftSize(fftSizeSrc) +, sampleRate(sampleRateSrc) +{ } + +int FFTModel::getFFTSize() const { + return fftSize; +} + +void FFTModel::setFFTSize(int fftSizeSrc) { + fftSize = fftSizeSrc; +} + +int FFTModel::getSampleRate() const { + return sampleRate; +} + +void FFTModel::setSampleRate(int sampleRateSrc) { + sampleRate = sampleRateSrc; +} + diff --git a/euphony/src/main/cpp/core/source/FFTProcessor.cpp b/euphony/src/main/cpp/core/source/FFTProcessor.cpp new file mode 100644 index 0000000..69c2a6e --- /dev/null +++ b/euphony/src/main/cpp/core/source/FFTProcessor.cpp @@ -0,0 +1,54 @@ +// +// Created by opener on 20. 11. 30. +// + +#include "../FFTProcessor.h" +#include "../Definitions.h" + +Euphony::FFTProcessor::FFTProcessor(int fft_size, int samplerate) +: FFTModel(fft_size, samplerate) +, fftSize(fft_size) +, halfOfFFTSize(fft_size >> 1) +{ + config = kiss_fftr_alloc(fft_size, 0, nullptr, nullptr); + spectrum = (kiss_fft_cpx*) malloc(sizeof(kiss_fft_cpx) * (int)fft_size); + result = new float[halfOfFFTSize](); +} + +Euphony::FFTProcessor::~FFTProcessor() { + free(config); + free(spectrum); + delete result; +} + +inline float Euphony::FFTProcessor::shortToFloat(const short val) { + if( val < 0 ) + return val * ( 1 / 32768.0f ); + else + return val * ( 1 / 32767.0f ); +} + + +inline int Euphony::FFTProcessor::frequencyToIndex(const int freq) const { + return (int)(((halfOfFFTSize) + 1) * ((double)freq / (double)getSampleRate())); +} + +float* Euphony::FFTProcessor::makeSpectrum(short* src) { + int startIdx = frequencyToIndex(kStartSignalFrequency) - 1; + int lenHalfOfNumSamples = halfOfFFTSize; // spectrum size must be half of numSamples; + + kiss_fftr(config, src, spectrum); + + for(int i = startIdx; i <= lenHalfOfNumSamples; ++i) { + float re = shortToFloat(spectrum[i].r) * (float)fftSize; + float im = shortToFloat(spectrum[i].i) * (float)fftSize; + + result[i] = sqrtf(re * re + im * im) / (float)lenHalfOfNumSamples; + } + + return result; +} + +int Euphony::FFTProcessor::getResultSize() const { + return halfOfFFTSize + 1; +} From c1294921ce4da041d49ac4150df4ac719b2d3a93 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 08:01:44 +0900 Subject: [PATCH 13/31] Added `FSK` modulation based on Modem > Added Modem interface > Implment FSK modulation > The Unit-test was passed (FSK) --- euphony/src/main/cpp/CMakeLists.txt | 1 + euphony/src/main/cpp/core/Definitions.h | 2 + euphony/src/main/cpp/core/FSK.h | 27 +++++ euphony/src/main/cpp/core/Modem.h | 25 ++++ euphony/src/main/cpp/core/source/FSK.cpp | 135 ++++++++++++++++++++++ euphony/src/main/cpp/tests/CMakeLists.txt | 1 + euphony/src/main/cpp/tests/FSKTest.cpp | 75 ++++++++++++ 7 files changed, 266 insertions(+) create mode 100644 euphony/src/main/cpp/core/FSK.h create mode 100644 euphony/src/main/cpp/core/Modem.h create mode 100644 euphony/src/main/cpp/core/source/FSK.cpp create mode 100644 euphony/src/main/cpp/tests/FSKTest.cpp diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index 4689f8f..fdcd61d 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -31,6 +31,7 @@ set(EUPHONY_SRC core/source/DefaultCharset.cpp core/source/FFTModel.cpp core/source/FFTProcessor.cpp + core/source/FSK.cpp core/source/HexVector.cpp core/source/Packet.cpp core/source/PacketBuilder.cpp diff --git a/euphony/src/main/cpp/core/Definitions.h b/euphony/src/main/cpp/core/Definitions.h index 571b95e..cfe4c1e 100644 --- a/euphony/src/main/cpp/core/Definitions.h +++ b/euphony/src/main/cpp/core/Definitions.h @@ -3,8 +3,10 @@ #include #include +#include "Wave.h" namespace Euphony { + typedef std::vector> WaveList; constexpr int32_t kChannelCount = 2; constexpr int32_t kSampleRate = 44100; diff --git a/euphony/src/main/cpp/core/FSK.h b/euphony/src/main/cpp/core/FSK.h new file mode 100644 index 0000000..dc87a34 --- /dev/null +++ b/euphony/src/main/cpp/core/FSK.h @@ -0,0 +1,27 @@ +#ifndef EUPHONY_FSK_H +#define EUPHONY_FSK_H + +#include "FFTModel.h" +#include "Modem.h" +#include +#include + +namespace Euphony { + class FSK : public Modem { + public: + FSK(); + WaveList modulate(std::string code); + WaveList modulate(Packet* packet); + static int getMaxIdxFromSource(const float* fft_source); + static int getMaxIdxFromSource(const float* fft_source, const int baseSize, const int sampleRate, const int fftSize); + std::shared_ptr demodulate(const WaveList& waveList); + std::shared_ptr demodulate(const float* source, int sourceLength, int bufferSize); + private: + std::unique_ptr fftModel; + static int getStartFreqIdx() ; + static int getEndFreqIdx() ; + static int getStartFreqIdx(const int sampleRate, const int fftSize); + static int getEndFreqIdx(const int sampleRate, const int fftSize); + }; +} +#endif //EUPHONY_FSK_H diff --git a/euphony/src/main/cpp/core/Modem.h b/euphony/src/main/cpp/core/Modem.h new file mode 100644 index 0000000..bf92eb6 --- /dev/null +++ b/euphony/src/main/cpp/core/Modem.h @@ -0,0 +1,25 @@ +#ifndef EUPHONY_MODEM_H +#define EUPHONY_MODEM_H + +#include +#include "Definitions.h" +#include "Packet.h" +#include "Wave.h" + +using std::string; +using std::vector; +using std::shared_ptr; + +namespace Euphony { + + class Modem { + public: + virtual ~Modem() = default; + virtual WaveList modulate(string code) = 0; + virtual WaveList modulate(Packet* packet) = 0; + virtual std::shared_ptr demodulate(const WaveList& waveList) = 0; + virtual std::shared_ptr demodulate(const float* source, int sourceLength, int bufferSize) = 0; + }; +} + +#endif //EUPHONY_MODEM_H diff --git a/euphony/src/main/cpp/core/source/FSK.cpp b/euphony/src/main/cpp/core/source/FSK.cpp new file mode 100644 index 0000000..c952e1b --- /dev/null +++ b/euphony/src/main/cpp/core/source/FSK.cpp @@ -0,0 +1,135 @@ +#include "../FSK.h" +#include "../FFTProcessor.h" +#include "../Definitions.h" +#include "../WaveBuilder.h" +#include "../Base16Exception.h" +#include "../Packet.h" +#include + +using namespace Euphony; + + +FSK::FSK() : fftModel(std::make_unique(kFFTSize, kSampleRate)){ } + +WaveList FSK::modulate(Packet* packet) +{ + return this->modulate(packet->toString()); +} + +WaveList FSK::modulate(string code) { + + vector> result; + + for (char c : code ) { + switch(c) { + case 'S': + result.push_back( + Wave::create() + .vibratesAt(kStartSignalFrequency) + .setSize(kBufferSize) + .setCrossfade(BOTH) + .build() + ); + break; + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': + result.push_back( + Wave::create() + .vibratesAt(kStandardFrequency + ((c - '0') * kFrequencyInterval)) + .setSize(kBufferSize) + .setCrossfade(BOTH) + .build() + ); + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + result.push_back( + Wave::create() + .vibratesAt(kStandardFrequency + ((c - 'a' + 10) * kFrequencyInterval)) + .setSize(kBufferSize) + .setCrossfade(BOTH) + .build() + ); + break; + default: + throw Base16Exception(); + } + } + + return result; +} + +int FSK::getMaxIdxFromSource(const float *fft_source) { + int maxIndex = 0; + float maxValue = 0; + const int startIdx = getStartFreqIdx(); + const int endIdx = getEndFreqIdx(); + for(int i = startIdx - 1; i < endIdx; i++) { + if(fft_source[i] > maxValue) { + maxValue = fft_source[i]; + maxIndex = i; + } + } + + return maxIndex - getStartFreqIdx(); +} + + +int FSK::getMaxIdxFromSource(const float *fft_source, const int baseSize, const int sampleRate, const int fftSize) { + int maxIndex = 0; + float maxValue = 0; + const int startIdx = getStartFreqIdx(sampleRate, fftSize); + const int endIdx = startIdx + baseSize; + for(int i = startIdx - 1; i < endIdx; i++) { + if(fft_source[i] > maxValue) { + maxValue = fft_source[i]; + maxIndex = i; + } + } + + return maxIndex - getStartFreqIdx(); +} + + +shared_ptr FSK::demodulate(const WaveList& waveList) { + HexVector hexVector = HexVector(waveList.size()); + + for(const auto& wave : waveList) { + auto vectorInt16Source = wave->getInt16Source(); + int16_t* int16Source = &vectorInt16Source[0]; + float *resultBuf = fftModel->makeSpectrum(int16Source); + hexVector.pushBack(getMaxIdxFromSource(resultBuf)); + } + + return std::make_shared(hexVector); +} + +std::shared_ptr FSK::demodulate(const float *source, int sourceLength, int bufferSize) { + int dataSize = sourceLength / bufferSize; + HexVector hexVector = HexVector(dataSize); + + WaveList waveList; + for(int i = 0; i < dataSize; i++) { + waveList.push_back(std::make_shared(source + (i * bufferSize), bufferSize)); + } + + return demodulate(waveList); +} + +int FSK::getStartFreqIdx() { + return std::lround((static_cast(static_cast(kStandardFrequency) / static_cast(kSampleRate >> 1)) * (kFFTSize >> 1))); +} + +int FSK::getStartFreqIdx(const int sampleRate, const int fftSize) { + return std::lround((static_cast(static_cast(kStandardFrequency) / static_cast(sampleRate >> 1)) * (fftSize >> 1))); +} + +int FSK::getEndFreqIdx() { + return std::lround((static_cast(static_cast(kStandardFrequency) / static_cast(kSampleRate >> 1)) * (kFFTSize >> 1))) + 16; +} + +int FSK::getEndFreqIdx(const int sampleRate, const int fftSize) { + return std::lround((static_cast(static_cast(kStandardFrequency) / static_cast(sampleRate >> 1)) * (fftSize >> 1))) + 16; +} diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index 5d32829..28a66eb 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable( base2Test.cpp base16Test.cpp defaultCharsetTest.cpp + FSKTest.cpp hexVectorTest.cpp packetTest.cpp packetBuilderTest.cpp diff --git a/euphony/src/main/cpp/tests/FSKTest.cpp b/euphony/src/main/cpp/tests/FSKTest.cpp new file mode 100644 index 0000000..dfc9e68 --- /dev/null +++ b/euphony/src/main/cpp/tests/FSKTest.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class FSKTestFixture : public ::testing::TestWithParam { + +public: + void createFSK() { + EXPECT_EQ(fsk, nullptr); + fsk = new FSK(); + ASSERT_NE(fsk, nullptr); + } + + void createFFT() { + EXPECT_EQ(fft, nullptr); + fft = new FFTProcessor(kFFTSize, kSampleRate); + ASSERT_NE(fft, nullptr); + } + + FSK* fsk = nullptr; + FFTProcessor* fft = nullptr; + +}; + +TEST_P(FSKTestFixture, FSKModulationString2StringTest) +{ + createFSK(); + + string inputCode; + int expectedCodeLength; + int expectedFreqIndex; + + std::tie(inputCode, expectedCodeLength, expectedFreqIndex) = GetParam(); + + auto modulateFSK = fsk->modulate(inputCode); + EXPECT_EQ(modulateFSK.size(), expectedCodeLength); + + auto demodulateResult = fsk->demodulate(modulateFSK); + EXPECT_EQ(inputCode, demodulateResult->getPayloadStr()); +} + +TEST_F(FSKTestFixture, FSKCodeThrowTest) +{ + createFSK(); + + string inputCode = "K"; + try { + auto resultFSK = fsk->modulate(inputCode); + } catch(Base16Exception e) { + EXPECT_EQ(BASE16_EXCEPTION_MSG, e.MSG()); + } +} + +INSTANTIATE_TEST_CASE_P( + FSKTestSuite, + FSKTestFixture, + ::testing::Values( + TestParamType("0", 1, 0), + TestParamType("1", 1, 1), + TestParamType("2", 1, 2), + TestParamType("3", 1, 3), + TestParamType("4", 1, 4), + TestParamType("5", 1, 5), + TestParamType("012345", 6, 0), + TestParamType("0123456789", 10, 0), + TestParamType("abcdef", 6, 10), + TestParamType("0123456789abcdef", 16, 0) +)); \ No newline at end of file From 32a849a1aafa15e43bc832744229f4f269e8afdf Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 08:05:29 +0900 Subject: [PATCH 14/31] Created Unit-test for `FFTProcessor` > The Unit-test is passed (FFTProcessor) --- euphony/src/main/cpp/tests/CMakeLists.txt | 1 + euphony/src/main/cpp/tests/FFTTest.cpp | 146 ++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 euphony/src/main/cpp/tests/FFTTest.cpp diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index 28a66eb..ba8d32d 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable( base2Test.cpp base16Test.cpp defaultCharsetTest.cpp + FFTTest.cpp FSKTest.cpp hexVectorTest.cpp packetTest.cpp diff --git a/euphony/src/main/cpp/tests/FFTTest.cpp b/euphony/src/main/cpp/tests/FFTTest.cpp new file mode 100644 index 0000000..aa204d8 --- /dev/null +++ b/euphony/src/main/cpp/tests/FFTTest.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class FFTTestFixture : public ::testing::TestWithParam { + +public: + std::unique_ptr fft = nullptr; +}; + +TEST_P(FFTTestFixture, FFTDefaultTest) +{ + int inputFrequency, inputFFTSize, inputSampleRate, expectedSpectrumIndex; + std::tie(inputFrequency, inputFFTSize, inputSampleRate, expectedSpectrumIndex) = GetParam(); + + fft = std::make_unique(inputFFTSize, inputSampleRate); + + auto wave = Wave::create() + .vibratesAt(inputFrequency) + .setSize(2048) + .build(); + + auto shortWaveSourceVector = wave->getInt16Source(); + int16_t* shortWaveSource = &shortWaveSourceVector[0]; + float* resultBuf = fft->makeSpectrum(shortWaveSource); + const int activeResult = FSK::getMaxIdxFromSource(resultBuf, 32, inputSampleRate, inputFFTSize); + + EXPECT_EQ(expectedSpectrumIndex, activeResult); +} + +INSTANTIATE_TEST_SUITE_P( + FFTTest, + FFTTestFixture, + ::testing::Values( + /* + * Frequency, FFTSize, sampleRate, expectedSpectrumIndex + * kStartSignalFrequency = 17950 + */ + TestParamType(17924, 512, 44100, -1), + TestParamType(18000, 512, 44100, 0), + TestParamType(18086, 512, 44100, 1), + TestParamType(18172, 512, 44100, 2), + TestParamType(18258, 512, 44100, 3), + TestParamType(18345, 512, 44100, 4), + TestParamType(18431, 512, 44100, 5), + TestParamType(18517, 512, 44100, 6), + TestParamType(18603, 512, 44100, 7), + TestParamType(18689, 512, 44100, 8), + TestParamType(18775, 512, 44100, 9), + TestParamType(18861, 512, 44100, 10), + TestParamType(18947, 512, 44100, 11), + TestParamType(19033, 512, 44100, 12), + TestParamType(19119, 512, 44100, 13), + TestParamType(19205, 512, 44100, 14), + TestParamType(19291, 512, 44100, 15), + TestParamType(19377, 512, 44100, 16), + TestParamType(19463, 512, 44100, 17), + TestParamType(19549, 512, 44100, 18), + TestParamType(19635, 512, 44100, 19), + TestParamType(19721, 512, 44100, 20), + TestParamType(19807, 512, 44100, 21), + TestParamType(19893, 512, 44100, 22), + TestParamType(19979, 512, 44100, 23), + TestParamType(20065, 512, 44100, 24), + TestParamType(20151, 512, 44100, 25), + TestParamType(20237, 512, 44100, 26), + TestParamType(20323, 512, 44100, 27), + TestParamType(20409, 512, 44100, 28), + TestParamType(20495, 512, 44100, 29), + TestParamType(20581, 512, 44100, 30), + TestParamType(20667, 512, 44100, 31), + TestParamType(17956, 512, 44100, -1), + TestParamType(17957, 512, 44100, -1), + TestParamType(17958, 512, 44100, -1), + TestParamType(17959, 512, 44100, 0), + TestParamType(17960, 512, 44100, 0), + TestParamType(18001, 512, 44100, 0), + TestParamType(18043, 512, 44100, 0), + TestParamType(18044, 512, 44100, 0), + TestParamType(18045, 512, 44100, 1), + TestParamType(18046, 512, 44100, 1), + TestParamType(18087, 512, 44100, 1), + TestParamType(18129, 512, 44100, 1), + TestParamType(18130, 512, 44100, 1), + TestParamType(18131, 512, 44100, 2), + TestParamType(18132, 512, 44100, 2), + TestParamType(18173, 512, 44100, 2), + TestParamType(18215, 512, 44100, 2), + TestParamType(18216, 512, 44100, 2), + TestParamType(18217, 512, 44100, 3), + TestParamType(18218, 512, 44100, 3), + TestParamType(18259, 512, 44100, 3), + TestParamType(18301, 512, 44100, 3), + TestParamType(18302, 512, 44100, 3), + TestParamType(18303, 512, 44100, 4), + TestParamType(18304, 512, 44100, 4), + TestParamType(18346, 512, 44100, 4), + TestParamType(18324, 512, 44100, 4), + TestParamType(18389, 512, 44100, 4), + TestParamType(18390, 512, 44100, 5), + TestParamType(18391, 512, 44100, 5), + TestParamType(18432, 512, 44100, 5), + TestParamType(18392, 512, 44100, 5), + TestParamType(18475, 512, 44100, 5), + TestParamType(18476, 512, 44100, 6), + TestParamType(18477, 512, 44100, 6), + TestParamType(18518, 512, 44100, 6), + TestParamType(18560, 512, 44100, 6), + TestParamType(18561, 512, 44100, 6), + TestParamType(18562, 512, 44100, 7), + TestParamType(18604, 512, 44100, 7), + TestParamType(18647, 512, 44100, 7), + TestParamType(18648, 512, 44100, 8), + TestParamType(18690, 512, 44100, 8), + TestParamType(18733, 512, 44100, 8), + TestParamType(18734, 512, 44100, 9), + TestParamType(18776, 512, 44100, 9), + TestParamType(18819, 512, 44100, 9), + TestParamType(18820, 512, 44100, 10), + TestParamType(18863, 512, 44100, 10), + TestParamType(18905, 512, 44100, 10), + TestParamType(18906, 512, 44100, 11), + TestParamType(18949, 512, 44100, 11), + TestParamType(18991, 512, 44100, 11), + TestParamType(18992, 512, 44100, 12), + TestParamType(19035, 512, 44100, 12), + TestParamType(19078, 512, 44100, 12), + TestParamType(19079, 512, 44100, 13), + TestParamType(19122, 512, 44100, 13), + TestParamType(19164, 512, 44100, 13), + TestParamType(19165, 512, 44100, 14), + TestParamType(19208, 512, 44100, 14), + TestParamType(19250, 512, 44100, 14), + TestParamType(19251, 512, 44100, 15), + TestParamType(19294, 512, 44100, 15), + TestParamType(19336, 512, 44100, 15), + TestParamType(19337, 512, 44100, 16) + ) +); \ No newline at end of file From a92f9f4648c5737ef793f2ceb71727191495d271 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 08:09:01 +0900 Subject: [PATCH 15/31] Created `ModemFactory` to create Modem's implmentation easily --- euphony/src/main/cpp/core/ModemFactory.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 euphony/src/main/cpp/core/ModemFactory.h diff --git a/euphony/src/main/cpp/core/ModemFactory.h b/euphony/src/main/cpp/core/ModemFactory.h new file mode 100644 index 0000000..ec17665 --- /dev/null +++ b/euphony/src/main/cpp/core/ModemFactory.h @@ -0,0 +1,21 @@ +#ifndef EUPHONY_MODEMFACTORY_H +#define EUPHONY_MODEMFACTORY_H + +#include "Definitions.h" +#include "Modem.h" +#include "FSK.h" + +namespace Euphony { + class ModemFactory { + public: + static std::shared_ptr create(ModulationType type) { + switch(type) { + default: + case ModulationType::FSK : + return std::make_shared(); + } + } + }; +} + +#endif //EUPHONY_MODEMFACTORY_H From 51c2dd883bae1b043e7f246b7c562356ce633b99 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 08:18:58 +0900 Subject: [PATCH 16/31] Added Oboe's prefab & necessary headers --- euphony/src/main/cpp/CMakeLists.txt | 9 +- .../sl-headers/DefaultAudioStreamCallback.h | 139 ++++++++++++++++++ .../cpp/arms/sl-headers/IRenderableAudio.h | 31 ++++ .../main/cpp/arms/sl-headers/IRestartable.h | 28 ++++ .../src/main/cpp/arms/sl-headers/ITappable.h | 25 ++++ euphony/src/main/cpp/arms/sl-headers/Mixer.h | 63 ++++++++ .../main/cpp/arms/sl-headers/MonoToStereo.h | 49 ++++++ .../src/main/cpp/arms/sl-headers/Oscillator.h | 94 ++++++++++++ .../cpp/arms/sl-headers/TappableAudioSource.h | 38 +++++ 9 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 euphony/src/main/cpp/arms/sl-headers/DefaultAudioStreamCallback.h create mode 100644 euphony/src/main/cpp/arms/sl-headers/IRenderableAudio.h create mode 100644 euphony/src/main/cpp/arms/sl-headers/IRestartable.h create mode 100644 euphony/src/main/cpp/arms/sl-headers/ITappable.h create mode 100644 euphony/src/main/cpp/arms/sl-headers/Mixer.h create mode 100644 euphony/src/main/cpp/arms/sl-headers/MonoToStereo.h create mode 100644 euphony/src/main/cpp/arms/sl-headers/Oscillator.h create mode 100644 euphony/src/main/cpp/arms/sl-headers/TappableAudioSource.h diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index fdcd61d..2502029 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -43,15 +43,22 @@ set(EUPHONY_SRC set(EUPHONY_HDR core) set(DEBUG_HELPER debug-helper) set(DEBUG_HELPER_SRC debug-helper/Trace.cpp) +set(SL_HEADERS arms/sl-headers) -include_directories( ${EUPHONY_HDR} ${DEBUG_HELPER}) +include_directories( ${EUPHONY_HDR} ${SL_HEADERS} ${DEBUG_HELPER}) add_library(euphony SHARED ${EUPHONY_SRC} ${DEBUG_HELPER_SRC}) target_compile_definitions(euphony PUBLIC FIXED_POINT=16) +find_package( + oboe REQUIRED CONFIG +) + target_link_libraries( euphony ${log-lib} + OpenSLES + oboe::oboe -fopenmp -static-openmp ) diff --git a/euphony/src/main/cpp/arms/sl-headers/DefaultAudioStreamCallback.h b/euphony/src/main/cpp/arms/sl-headers/DefaultAudioStreamCallback.h new file mode 100644 index 0000000..d717dce --- /dev/null +++ b/euphony/src/main/cpp/arms/sl-headers/DefaultAudioStreamCallback.h @@ -0,0 +1,139 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SAMPLES_DEFAULT_AUDIO_STREAM_CALLBACK_H +#define SAMPLES_DEFAULT_AUDIO_STREAM_CALLBACK_H + + +#include +#include +#include + +#include +#include + +/** + * This is a callback object which will render data from an `IRenderableAudio` source. It is + * constructed using an `IRestartable` which allows it to automatically restart the parent object + * if the stream is disconnected (for example, when headphones are attached). + * + * @param IRestartable - the object which should be restarted when the stream is disconnected + */ +class DefaultAudioStreamCallback : public oboe::AudioStreamCallback { +public: + DefaultAudioStreamCallback(IRestartable &parent): mParent(parent) {} + virtual ~DefaultAudioStreamCallback() = default; + + virtual oboe::DataCallbackResult + onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override { + + if (mIsThreadAffinityEnabled && !mIsThreadAffinitySet) { + setThreadAffinity(); + mIsThreadAffinitySet = true; + } + + float *outputBuffer = static_cast(audioData); + if (!mRenderable) { + LOGE("Renderable source not set!"); + return oboe::DataCallbackResult::Stop; + } + mRenderable->renderAudio(outputBuffer, numFrames); + return oboe::DataCallbackResult::Continue; + } + virtual void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result error) override { + // Restart the stream when it errors out with disconnect + if (error == oboe::Result::ErrorDisconnected) { + LOGE("Restarting AudioStream after disconnect"); + mParent.restart(); + } else { + LOGE("Unknown error"); + } + mIsThreadAffinitySet = false; + } + + void setSource(std::shared_ptr renderable) { + mRenderable = renderable; + } + + std::shared_ptr getSource() { + return mRenderable; + } + + /** + * Set the CPU IDs to bind the audio callback thread to + * + * @param mCpuIds - the CPU IDs to bind to + */ + void setCpuIds(std::vector cpuIds){ + mCpuIds = std::move(cpuIds); + } + + /** + * Enable or disable binding the audio callback thread to specific CPU cores. The CPU core IDs + * can be specified using @see setCpuIds. If no CPU IDs are specified the initial core which the + * audio thread is called on will be used. + * + * @param isEnabled - whether the audio callback thread should be bound to specific CPU core(s) + */ + void setThreadAffinityEnabled(bool isEnabled){ + mIsThreadAffinityEnabled = isEnabled; + LOGD("Thread affinity enabled: %s", (isEnabled) ? "true" : "false"); + } + +private: + std::shared_ptr mRenderable; + IRestartable &mParent; + std::vector mCpuIds; // IDs of CPU cores which the audio callback should be bound to + std::atomic mIsThreadAffinityEnabled { false }; + std::atomic mIsThreadAffinitySet { false }; + + /** + * Set the thread affinity for the current thread to mCpuIds. This can be useful to call on the + * audio thread to avoid underruns caused by CPU core migrations to slower CPU cores. + */ + void setThreadAffinity() { + + pid_t current_thread_id = gettid(); + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + + // If the callback cpu ids aren't specified then bind to the current cpu + if (mCpuIds.empty()) { + int current_cpu_id = sched_getcpu(); + LOGD("Binding to current CPU ID %d", current_cpu_id); + CPU_SET(current_cpu_id, &cpu_set); + } else { + LOGD("Binding to %d CPU IDs", static_cast(mCpuIds.size())); + for (size_t i = 0; i < mCpuIds.size(); i++) { + int cpu_id = mCpuIds.at(i); + LOGD("CPU ID %d added to cores set", cpu_id); + CPU_SET(cpu_id, &cpu_set); + } + } + + int result = sched_setaffinity(current_thread_id, sizeof(cpu_set_t), &cpu_set); + if (result == 0) { + LOGV("Thread affinity set"); + } else { + LOGW("Error setting thread affinity. Error no: %d", result); + } + + mIsThreadAffinitySet = true; + } + +}; + +#endif //SAMPLES_DEFAULT_AUDIO_STREAM_CALLBACK_H diff --git a/euphony/src/main/cpp/arms/sl-headers/IRenderableAudio.h b/euphony/src/main/cpp/arms/sl-headers/IRenderableAudio.h new file mode 100644 index 0000000..da0b8cb --- /dev/null +++ b/euphony/src/main/cpp/arms/sl-headers/IRenderableAudio.h @@ -0,0 +1,31 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SAMPLES_IRENDERABLEAUDIO_H +#define SAMPLES_IRENDERABLEAUDIO_H + +#include +#include + +class IRenderableAudio { + +public: + virtual ~IRenderableAudio() = default; + virtual void renderAudio(float *audioData, int32_t numFrames) = 0; +}; + + +#endif //SAMPLES_IRENDERABLEAUDIO_H diff --git a/euphony/src/main/cpp/arms/sl-headers/IRestartable.h b/euphony/src/main/cpp/arms/sl-headers/IRestartable.h new file mode 100644 index 0000000..855fff3 --- /dev/null +++ b/euphony/src/main/cpp/arms/sl-headers/IRestartable.h @@ -0,0 +1,28 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SAMPLES_IRESTARTABLE_H +#define SAMPLES_IRESTARTABLE_H + +/** + * Represents an object which can be restarted. For example an audio engine which has one or more + * streams which can be restarted following a change in audio device configuration. For example, + * headphones being connected. + */ +class IRestartable { +public: + virtual void restart() = 0; +}; +#endif //SAMPLES_IRESTARTABLE_H diff --git a/euphony/src/main/cpp/arms/sl-headers/ITappable.h b/euphony/src/main/cpp/arms/sl-headers/ITappable.h new file mode 100644 index 0000000..40cb716 --- /dev/null +++ b/euphony/src/main/cpp/arms/sl-headers/ITappable.h @@ -0,0 +1,25 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SAMPLES_ITAPPABLE_H +#define SAMPLES_ITAPPABLE_H + +class ITappable { +public: + virtual ~ITappable() = default; + virtual void tap(bool isDown) = 0; +}; +#endif //SAMPLES_ITAPPABLE_H diff --git a/euphony/src/main/cpp/arms/sl-headers/Mixer.h b/euphony/src/main/cpp/arms/sl-headers/Mixer.h new file mode 100644 index 0000000..56926d8 --- /dev/null +++ b/euphony/src/main/cpp/arms/sl-headers/Mixer.h @@ -0,0 +1,63 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SHARED_MIXER_H +#define SHARED_MIXER_H + +#include +#include "IRenderableAudio.h" + +constexpr int32_t kBufferSize = 192*10; // Temporary buffer is used for mixing +constexpr uint8_t kMaxTracks = 100; + +/** + * A Mixer object which sums the output from multiple tracks into a single output. The number of + * input channels on each track must match the number of output channels (default 1=mono). This can + * be changed by calling `setChannelCount`. + * The inputs to the mixer are not owned by the mixer, they should not be deleted while rendering. + */ +class Mixer : public IRenderableAudio { + +public: + void renderAudio(float *audioData, int32_t numFrames) { + + // Zero out the incoming container array + memset(audioData, 0, sizeof(float) * numFrames * mChannelCount); + + for (int i = 0; i < mNextFreeTrackIndex; ++i) { + mTracks[i]->renderAudio(mixingBuffer, numFrames); + + for (int j = 0; j < numFrames * mChannelCount; ++j) { + audioData[j] += mixingBuffer[j]; + } + } + } + + void addTrack(IRenderableAudio *renderer){ + mTracks[mNextFreeTrackIndex++] = renderer; + } + + void setChannelCount(int32_t channelCount){ mChannelCount = channelCount; } + +private: + float mixingBuffer[kBufferSize]; + std::array mTracks; + uint8_t mNextFreeTrackIndex = 0; + int32_t mChannelCount = 1; // Default to mono +}; + + +#endif //SHARED_MIXER_H diff --git a/euphony/src/main/cpp/arms/sl-headers/MonoToStereo.h b/euphony/src/main/cpp/arms/sl-headers/MonoToStereo.h new file mode 100644 index 0000000..97ba349 --- /dev/null +++ b/euphony/src/main/cpp/arms/sl-headers/MonoToStereo.h @@ -0,0 +1,49 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SHARED_MONOTOSTEREO_H +#define SHARED_MONOTOSTEREO_H + +#include "IRenderableAudio.h" + + +class MonoToStereo : public IRenderableAudio { + +public: + + MonoToStereo(IRenderableAudio *input) : mInput(input){}; + + void renderAudio(float *audioData, int32_t numFrames) override { + + constexpr int kChannelCountStereo = 2; + + mInput->renderAudio(audioData, numFrames); + + // We assume that audioData has sufficient frames to hold the stereo output, so copy each + // frame in the input to the output twice, working our way backwards through the input array + // e.g. 123 => 112233 + for (int i = numFrames - 1; i >= 0; --i) { + + audioData[i * kChannelCountStereo] = audioData[i]; + audioData[i * kChannelCountStereo + 1] = audioData[i]; + } + } + + IRenderableAudio *mInput; +}; + + +#endif //SHARED_MONOTOSTEREO_H diff --git a/euphony/src/main/cpp/arms/sl-headers/Oscillator.h b/euphony/src/main/cpp/arms/sl-headers/Oscillator.h new file mode 100644 index 0000000..4847274 --- /dev/null +++ b/euphony/src/main/cpp/arms/sl-headers/Oscillator.h @@ -0,0 +1,94 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef SHARED_OSCILLATOR_H +#define SHARED_OSCILLATOR_H + + +#include +#include +#include +#include +#include "IRenderableAudio.h" + +constexpr double kDefaultFrequency = 440.0; +constexpr int32_t kDefaultSampleRate = 48000; +constexpr double kPi = M_PI; +constexpr double kTwoPi = kPi * 2; + +class Oscillator : public IRenderableAudio { + +public: + + ~Oscillator() = default; + + void setWaveOn(bool isWaveOn) { + mIsWaveOn.store(isWaveOn); + }; + + void setSampleRate(int32_t sampleRate) { + mSampleRate = sampleRate; + updatePhaseIncrement(); + }; + + void setFrequency(double frequency) { + mFrequency = frequency; + updatePhaseIncrement(); + }; + + inline void setAmplitude(float amplitude) { + mAmplitude = amplitude; + }; + + // From IRenderableAudio + void renderAudio(float *audioData, int32_t numFrames) override { + + if (mIsWaveOn){ + for (int i = 0; i < numFrames; ++i) { + + // Sine wave (sinf) + //data[i*kChannelCount] = sinf(mPhase) * mAmplitude; + + // Square wave + if (mPhase <= kPi){ + audioData[i] = -mAmplitude; + } else { + audioData[i] = mAmplitude; + } + + mPhase += mPhaseIncrement; + if (mPhase > kTwoPi) mPhase -= kTwoPi; + } + } else { + memset(audioData, 0, sizeof(float) * numFrames); + } + }; + +private: + std::atomic mIsWaveOn { false }; + float mPhase = 0.0; + std::atomic mAmplitude { 0 }; + std::atomic mPhaseIncrement { 0.0 }; + double mFrequency = kDefaultFrequency; + int32_t mSampleRate = kDefaultSampleRate; + + void updatePhaseIncrement(){ + mPhaseIncrement.store((kTwoPi * mFrequency) / static_cast(mSampleRate)); + }; +}; + +#endif //SHARED_OSCILLATOR_H diff --git a/euphony/src/main/cpp/arms/sl-headers/TappableAudioSource.h b/euphony/src/main/cpp/arms/sl-headers/TappableAudioSource.h new file mode 100644 index 0000000..fde3917 --- /dev/null +++ b/euphony/src/main/cpp/arms/sl-headers/TappableAudioSource.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SAMPLES_RENDERABLE_TAP_H +#define SAMPLES_RENDERABLE_TAP_H + +#include + +#include "IRenderableAudio.h" +#include "ITappable.h" + +/** + * This class renders Float audio, but can be tapped to control. + * It also contains members for sample rate and channel count + */ +class TappableAudioSource : public IRenderableAudio, public ITappable { +public: + TappableAudioSource(int32_t sampleRate, int32_t channelCount) : + mSampleRate(sampleRate), mChannelCount(channelCount) { } + + int32_t mSampleRate; + int32_t mChannelCount; +}; + +#endif //SAMPLES_RENDERABLE_TAP_H \ No newline at end of file From 8de813966a78cb1746c6c61bca692af01e268d89 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 08:36:44 +0900 Subject: [PATCH 17/31] Created Euphony Native Interface through JNI > Made EuPI concept on TX. > Created EuPIOscillator & EuPIRenderer > Created WaveRenderer for acoustic data communication > Created Native Interface using Oboe through JNI --- euphony/src/main/cpp/CMakeLists.txt | 6 + .../src/main/cpp/core/AudioStreamCallback.h | 58 +++ euphony/src/main/cpp/core/EuPIOscillator.h | 62 ++++ euphony/src/main/cpp/core/EuPIRenderer.h | 34 ++ .../src/main/cpp/core/EuphonyAudioSource.h | 11 + euphony/src/main/cpp/core/TxEngine.h | 52 +++ euphony/src/main/cpp/core/WaveRenderer.h | 32 ++ .../cpp/core/source/AudioStreamCallback.cpp | 31 ++ .../main/cpp/core/source/EuPIOscillator.cpp | 51 +++ .../src/main/cpp/core/source/EuPIRenderer.cpp | 58 +++ euphony/src/main/cpp/core/source/TxEngine.cpp | 340 ++++++++++++++++++ .../src/main/cpp/core/source/WaveRenderer.cpp | 82 +++++ euphony/src/main/cpp/native-connector.cpp | 305 ++++++++++++++++ .../co/euphony/tx/EuTxNativeConnector.java | 227 ++++++++++++ 14 files changed, 1349 insertions(+) create mode 100644 euphony/src/main/cpp/core/AudioStreamCallback.h create mode 100644 euphony/src/main/cpp/core/EuPIOscillator.h create mode 100644 euphony/src/main/cpp/core/EuPIRenderer.h create mode 100644 euphony/src/main/cpp/core/EuphonyAudioSource.h create mode 100644 euphony/src/main/cpp/core/TxEngine.h create mode 100644 euphony/src/main/cpp/core/WaveRenderer.h create mode 100644 euphony/src/main/cpp/core/source/AudioStreamCallback.cpp create mode 100644 euphony/src/main/cpp/core/source/EuPIOscillator.cpp create mode 100644 euphony/src/main/cpp/core/source/EuPIRenderer.cpp create mode 100644 euphony/src/main/cpp/core/source/TxEngine.cpp create mode 100644 euphony/src/main/cpp/core/source/WaveRenderer.cpp create mode 100644 euphony/src/main/cpp/native-connector.cpp create mode 100644 euphony/src/main/java/co/euphony/tx/EuTxNativeConnector.java diff --git a/euphony/src/main/cpp/CMakeLists.txt b/euphony/src/main/cpp/CMakeLists.txt index 2502029..5135069 100644 --- a/euphony/src/main/cpp/CMakeLists.txt +++ b/euphony/src/main/cpp/CMakeLists.txt @@ -24,11 +24,14 @@ target_link_libraries( set(EUPHONY_SRC arms/kiss_fft.c arms/kiss_fftr.c + core/source/AudioStreamCallback.cpp core/source/ASCIICharset.cpp core/source/Base2.cpp core/source/Base16.cpp core/source/Base16Exception.cpp core/source/DefaultCharset.cpp + core/source/EuPIOscillator.cpp + core/source/EuPIRenderer.cpp core/source/FFTModel.cpp core/source/FFTProcessor.cpp core/source/FSK.cpp @@ -36,8 +39,11 @@ set(EUPHONY_SRC core/source/Packet.cpp core/source/PacketBuilder.cpp core/source/PacketErrorDetector.cpp + core/source/TxEngine.cpp core/source/Wave.cpp core/source/WaveBuilder.cpp + core/source/WaveRenderer.cpp + native-connector.cpp ) set(EUPHONY_HDR core) diff --git a/euphony/src/main/cpp/core/AudioStreamCallback.h b/euphony/src/main/cpp/core/AudioStreamCallback.h new file mode 100644 index 0000000..2ed4718 --- /dev/null +++ b/euphony/src/main/cpp/core/AudioStreamCallback.h @@ -0,0 +1,58 @@ +// +// Created by designe on 20. 8. 24. +// + +#ifndef EUPHONY_AUDIOSTREAMCALLBACK_H +#define EUPHONY_AUDIOSTREAMCALLBACK_H + +#include +#include + +#include +#include +#include + +/** + * This callback object extends the functionality of `DefaultAudioStreamCallback` by automatically + * tuning the latency of the audio stream. @see onAudioReady for more details on this. + * + * It also demonstrates how to use tracing functions for logging inside the audio callback without + * blocking. + */ + +namespace Euphony { + class AudioStreamCallback : public DefaultAudioStreamCallback { + public: + AudioStreamCallback(IRestartable &mParent) : DefaultAudioStreamCallback(mParent) { + + // Initialize the trace functions, this enables you to output trace statements without + // blocking. See https://developer.android.com/studio/profile/systrace-commandline.html + Trace::initialize(); + } + + /** + * Every time the playback stream requires data this method will be called. + * + * @param audioStream the audio stream which is requesting data, this is the mPlayStream object + * @param audioData an empty buffer into which we can write our audio data + * @param numFrames the number of audio frames which are required + * @return Either oboe::DataCallbackResult::Continue if the stream should continue requesting data + * or oboe::DataCallbackResult::Stop if the stream should stop. + */ + oboe::DataCallbackResult + onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override; + + void setBufferTuneEnabled(bool enabled) { mBufferTuneEnabled = enabled; } + + void useStream(std::shared_ptr stream); + + private: + bool mBufferTuneEnabled = true; + + // This will be used to automatically tune the buffer size of the stream, obtaining optimal latency + std::unique_ptr mLatencyTuner; + oboe::AudioStream *mStream = nullptr; + }; +} + +#endif //EUPHONY_AUDIOSTREAMCALLBACK_H diff --git a/euphony/src/main/cpp/core/EuPIOscillator.h b/euphony/src/main/cpp/core/EuPIOscillator.h new file mode 100644 index 0000000..e6c51df --- /dev/null +++ b/euphony/src/main/cpp/core/EuPIOscillator.h @@ -0,0 +1,62 @@ +// +// Created by designe on 20. 8. 25. +// + +#ifndef EUPHONY_EUPIOSCILLATOR_H +#define EUPHONY_EUPIOSCILLATOR_H + +#include "Definitions.h" +#include +#include +#include +#include +#include +#include + +namespace Euphony { + class EuPIOscillator : public IRenderableAudio { + public: + ~EuPIOscillator() = default; + void setWaveOn(bool isWaveOn); + void setSampleRate(int32_t sampleRate); + void setFrequency(double frequency); + inline void setAmplitude(float amplitude) { + mAmplitude = amplitude; + } + // From IRenderableAudio + void renderAudio(float *data, int32_t numFrames); + static double getPhaseIncrement(double frequency){ + return ((kTwoPi * frequency) / static_cast (kSampleRate)); + } + static std::unique_ptr makeStaticWave(int freq, int waveLength) { + std::unique_ptr source = std::make_unique(waveLength); + + double phase = 0.0; + double phaseIncrement = Euphony::EuPIOscillator::getPhaseIncrement(freq); + for(int i = 0; i < waveLength; i++) { + source[i] = sin(phase); + phase += phaseIncrement; + if(phase > kTwoPi) phase -= kTwoPi; + } + + return std::unique_ptr(); + } + + private: + std::atomic mIsFirstWave{false}; + std::atomic mIsLastWave{false}; + std::atomic mIsWaveOn{false}; + float mPhase = 0.0; + std::atomic mAmplitude{0}; + std::atomic mPhaseIncrement{0.0}; + double mFrequency = 0.0; + std::vector mTimeArray; + int32_t mSampleRate = kSampleRate; + + void updatePhaseIncrement() { + mPhaseIncrement.store((kTwoPi * mFrequency) / static_cast(mSampleRate)); + } + }; +} + +#endif //EUPHONY_EUPIOSCILLATOR_H diff --git a/euphony/src/main/cpp/core/EuPIRenderer.h b/euphony/src/main/cpp/core/EuPIRenderer.h new file mode 100644 index 0000000..dc2baac --- /dev/null +++ b/euphony/src/main/cpp/core/EuPIRenderer.h @@ -0,0 +1,34 @@ +// +// Created by desig on 2020-08-15. +// + +#ifndef EUPHONY_EUPIRENDERER_H +#define EUPHONY_EUPIRENDERER_H + + +#include "EuPIOscillator.h" +#include "EuphonyAudioSource.h" + +namespace Euphony { + class EuPIRenderer : public EuphonyAudioSource { + public: + EuPIRenderer(int32_t sampleRate, int32_t channelCount); + EuPIRenderer(EuPIRenderer &&other) = default; + EuPIRenderer &operator=(EuPIRenderer &&other) = default; + ~EuPIRenderer() = default; + + void tap(bool isDown) override; + void renderAudio(float *audioData, int32_t numFrames) override; // from IRenderableAudio + void setFrequency(double frequency); + std::unique_ptr makeStaticWave(int freq); + + private: + std::unique_ptr mOscillators; + std::unique_ptr mBuffer = std::make_unique(kBufferSize); + int32_t mChannelCount; + int32_t mSampleRate; + }; +} + + +#endif //EUPHONY_EUPIRENDERER_H diff --git a/euphony/src/main/cpp/core/EuphonyAudioSource.h b/euphony/src/main/cpp/core/EuphonyAudioSource.h new file mode 100644 index 0000000..1ff5303 --- /dev/null +++ b/euphony/src/main/cpp/core/EuphonyAudioSource.h @@ -0,0 +1,11 @@ +#ifndef EUPHONY_EUPHONYAUDIOSOURCE_H +#define EUPHONY_EUPHONYAUDIOSOURCE_H + +#include "IRenderableAudio.h" + +class EuphonyAudioSource : public IRenderableAudio { +public: + ~EuphonyAudioSource() = default; + virtual void tap(bool isDown) = 0; +}; +#endif //EUPHONY_EUPHONYAUDIOSOURCE_H diff --git a/euphony/src/main/cpp/core/TxEngine.h b/euphony/src/main/cpp/core/TxEngine.h new file mode 100644 index 0000000..e432088 --- /dev/null +++ b/euphony/src/main/cpp/core/TxEngine.h @@ -0,0 +1,52 @@ +// +// Created by desig on 2020-08-15. +// + +#ifndef EUPHONY_TXENGINE_H +#define EUPHONY_TXENGINE_H + +#include + +constexpr int32_t kBufferSizeAutomatic = 0; + +namespace Euphony { + enum Status { + RUNNING, STOP + }; + + class TxEngine { + public: + TxEngine(); + ~TxEngine(); + + void tap(bool isDown); + void tapCount(bool isDown, int count); + void stop(); + void start(); + void setCode(std::string data); + std::string getCode(); + std::string getGenCode(); + void setCodingType(int codingTypeSrc); + void setMode(int modeSrc); + void setModulation(int modulationTypeSrc); + void setAudioApi(oboe::AudioApi audioApi); + void setChannelCount(int channelCount); + void setDeviceId(int32_t deviceId); + int getFramesPerBursts(); + void setBufferSizeInBursts(int32_t numBursts); + void setPerformance(oboe::PerformanceMode mode); + void setEupiFrequency(double freq); + double getCurrentOutputLatencyMillis(); + Status getStatus(); + bool isLatencyDetectionSupported(); + + private: + //Do Not Copy EpnyTxEngine + TxEngine(const TxEngine &); + const TxEngine &operator=(const TxEngine &); + class TxEngineImpl; + std::unique_ptr pImpl; + }; +} + +#endif //EUPHONY_TXENGINE_H diff --git a/euphony/src/main/cpp/core/WaveRenderer.h b/euphony/src/main/cpp/core/WaveRenderer.h new file mode 100644 index 0000000..42a3a23 --- /dev/null +++ b/euphony/src/main/cpp/core/WaveRenderer.h @@ -0,0 +1,32 @@ +#ifndef EUPHONY_WAVERENDERER_H +#define EUPHONY_WAVERENDERER_H + +#include "Definitions.h" +#include "EuphonyAudioSource.h" + +namespace Euphony { + + class WaveRenderer : public EuphonyAudioSource{ + public: + WaveRenderer(WaveList waveListSrc, int32_t channelCountSrc); + ~WaveRenderer() = default; + + void renderAudio(float *targetData, int32_t numFrames) override; //From IRenderableAudio + void tap(bool isDown) override; + void tapCount(bool isDown, int count); + float* getWaveSource(); + int32_t getWaveSourceSize() const; + void setWaveList(WaveList waveListSrc); + + private: + void renderSilence(float *targetData, int32_t numFrames); + std::unique_ptr waveSource; + std::atomic isWaveOn { false }; + int32_t channelCount; + int32_t readFrameIndex; + int32_t waveSourceSize; + int32_t renderIndex; + int32_t renderTotalCount; + }; +} +#endif //EUPHONY_WAVERENDERER_H diff --git a/euphony/src/main/cpp/core/source/AudioStreamCallback.cpp b/euphony/src/main/cpp/core/source/AudioStreamCallback.cpp new file mode 100644 index 0000000..f962eba --- /dev/null +++ b/euphony/src/main/cpp/core/source/AudioStreamCallback.cpp @@ -0,0 +1,31 @@ +// +// Created by designe on 20. 8. 24. +// + +#include + +oboe::DataCallbackResult Euphony::AudioStreamCallback::onAudioReady( + oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) { + if (oboeStream != mStream) { + mStream = oboeStream; + mLatencyTuner = std::make_unique(*oboeStream); + } + if (mBufferTuneEnabled + && mLatencyTuner + && oboeStream->getAudioApi() == oboe::AudioApi::AAudio) { + mLatencyTuner->tune(); + } + auto underrunCountResult = oboeStream->getXRunCount(); + int bufferSize = oboeStream->getBufferSizeInFrames(); + /** + * The following output can be seen by running a systrace. Tracing is preferable to logging + * inside the callback since tracing does not block. + * + * See https://developer.android.com/studio/profile/systrace-commandline.html + */ + if (Trace::isEnabled()) Trace::beginSection("numFrames %d, Underruns %d, buffer size %d", + numFrames, underrunCountResult.value(), bufferSize); + auto result = DefaultAudioStreamCallback::onAudioReady(oboeStream, audioData, numFrames); + if (Trace::isEnabled()) Trace::endSection(); + return result; +} \ No newline at end of file diff --git a/euphony/src/main/cpp/core/source/EuPIOscillator.cpp b/euphony/src/main/cpp/core/source/EuPIOscillator.cpp new file mode 100644 index 0000000..4712133 --- /dev/null +++ b/euphony/src/main/cpp/core/source/EuPIOscillator.cpp @@ -0,0 +1,51 @@ +// +// Created by designe on 20. 8. 25. +// + +#include "../EuPIOscillator.h" + +/* + * Frequencies Methods + * */ +void Euphony::EuPIOscillator::setFrequency(double frequency) { + mFrequency = frequency; + mPhaseIncrement.store((kTwoPi * mFrequency) / static_cast(mSampleRate)); +} + +void Euphony::EuPIOscillator::setSampleRate(int32_t sampleRate) { + mSampleRate = sampleRate; + mPhaseIncrement.store((kTwoPi * mFrequency) / static_cast(mSampleRate)); +} + +void Euphony::EuPIOscillator::setWaveOn(bool isWaveOn) { + mIsWaveOn.store(isWaveOn); +} + +void Euphony::EuPIOscillator::renderAudio(float *data, int32_t numFrames) { + if(mIsWaveOn) { + for(int i = 0; i < numFrames; ++i) { + data[i] = (float) (sin(mPhase) * mAmplitude); + mPhase += mPhaseIncrement; + if (mPhase > kTwoPi) mPhase -= kTwoPi; + } + if(mIsFirstWave != true) { + /* Crossfade in first */ + for(int i = 0; i < numFrames; i++) { + data[i] *= ((float)i / (float)numFrames); + } + } + mIsFirstWave.store(true); + mIsLastWave.store(false); + } else { + if(mIsLastWave != true) { + for(int i = 0; i < numFrames; ++i) { + data[i] = (sin(mPhase) * ((float)(numFrames-i) / (float)numFrames)); + mPhase += mPhaseIncrement; + if (mPhase > kTwoPi) mPhase -= kTwoPi; + } + mIsLastWave.store(true); + mIsFirstWave.store(false); + } + else memset(data, 0, sizeof(float) * numFrames); + } +} diff --git a/euphony/src/main/cpp/core/source/EuPIRenderer.cpp b/euphony/src/main/cpp/core/source/EuPIRenderer.cpp new file mode 100644 index 0000000..d5c2f08 --- /dev/null +++ b/euphony/src/main/cpp/core/source/EuPIRenderer.cpp @@ -0,0 +1,58 @@ +// +// Created by desig on 2020-08-15. +// + +#include "EuPIRenderer.h" + +using namespace Euphony; + +EuPIRenderer::EuPIRenderer(int32_t sampleRate, int32_t channelCount) + : mSampleRate(sampleRate), + mChannelCount(channelCount), + mOscillators(std::make_unique(channelCount)){ + + constexpr float amplitude = 1.0; + + // Set up the oscillators + for (int i = 0; i < mChannelCount; ++i) { + mOscillators[i].setFrequency(kStandardFrequency); + mOscillators[i].setSampleRate(mSampleRate); + mOscillators[i].setAmplitude(amplitude); + } +} + +void EuPIRenderer::renderAudio(float *audioData, int32_t numFrames) { + // Render each oscillator into its own channel + std::fill_n(mBuffer.get(), kBufferSize, 0); + for (int i = 0; i < mChannelCount; ++i) { + mOscillators[i].renderAudio(mBuffer.get(), numFrames); + for (int j = 0; j < numFrames; ++j) { + audioData[(j * mChannelCount) + i] = mBuffer[j]; + } + } +} + +void EuPIRenderer::setFrequency(double frequency) { + for(int i = 0; i< mChannelCount; ++i) { + mOscillators[i].setFrequency(frequency); + } +} + +void EuPIRenderer::tap(bool isDown) { + for (int i = 0; i < mChannelCount; ++i) { + mOscillators[i].setWaveOn(isDown); + } +} + +std::unique_ptr EuPIRenderer::makeStaticWave(int freq) { + std::unique_ptr waveArray = std::make_unique(kBufferSize); + float phase = 0.0; + double phaseIncrement = (kTwoPi * freq) / static_cast(kSampleRate); + for (int i = 0; i < kBufferSize; i++) { + waveArray[i] = sin(phase); + phase += phaseIncrement; + if (phase > kTwoPi) phase -= kTwoPi; + } + + return waveArray; +} \ No newline at end of file diff --git a/euphony/src/main/cpp/core/source/TxEngine.cpp b/euphony/src/main/cpp/core/source/TxEngine.cpp new file mode 100644 index 0000000..f6e1dfc --- /dev/null +++ b/euphony/src/main/cpp/core/source/TxEngine.cpp @@ -0,0 +1,340 @@ +// +// Created by desig on 2020-08-15. +// +#include + +#include +#include +#include "../Base2.h" +#include "../ModemFactory.h" +#include "../PacketBuilder.h" +#include "../TxEngine.h" +#include "../EuPIRenderer.h" +#include "../AudioStreamCallback.h" +#include "../WaveRenderer.h" + +using namespace Euphony; + +class TxEngine::TxEngineImpl : public IRestartable{ +public: + std::mutex mLock; + std::shared_ptr mStream; + oboe::AudioStreamBuilder mStreamBuilder; + std::unique_ptr mCallback; + shared_ptr mAudioSource = nullptr; + bool mIsLatencyDetectionSupported = false; + + double eupiFreq; + int32_t mDeviceId = oboe::Unspecified; + int32_t mChannelCount = oboe::Unspecified; + oboe::AudioApi mAudioApi = oboe::AudioApi::Unspecified; + + std::shared_ptr txPacket = nullptr; + std::shared_ptr mModem = nullptr; + ModulationType mModulationType; + BaseType mBaseCodingType; + ModeType mModeType; + Status mStatus; + + TxEngineImpl() + : mModulationType(ModulationType::FSK) + , mBaseCodingType(BaseType::BASE16) + , mModeType(ModeType::DEFAULT) + , mStatus(STOP){ + createCallback(); + setModulation(ModulationType::FSK); + //start(); + } + + virtual ~TxEngineImpl() = default; + + void createCallback() { + mCallback = std::make_unique(*this); + } + + oboe::Result createPlaybackStream() { + return mStreamBuilder.setSharingMode(oboe::SharingMode::Exclusive) + ->setPerformanceMode(oboe::PerformanceMode::LowLatency) + ->setFormat(oboe::AudioFormat::Float) + ->setCallback(mCallback.get()) + ->setChannelCount(kChannelCount) + ->setSampleRate(kSampleRate) + ->setDeviceId(mDeviceId) + ->openStream(mStream); + } + + std::shared_ptr createAudioSource(ModeType modeType) { + switch(modeType) { + default: + case ModeType::DEFAULT: { + auto modulationResult = mModem->modulate(txPacket->toString()); + return std::make_shared(modulationResult, kChannelCount); + } + case ModeType::EUPI: + mAudioSource = std::make_shared(kSampleRate, kChannelCount); + std::dynamic_pointer_cast(mAudioSource)->setFrequency(eupiFreq); + return mAudioSource; + } + } + + void setPerformance(oboe::PerformanceMode mode) { + mStreamBuilder.setPerformanceMode(mode)->openStream(mStream); + } + + void restart() override { + start(); + } + + void stop() { + std::lock_guard lock(mLock); + if(mStream) { + mStream->stop(); + mStatus = STOP; + } + } + + oboe::Result reopenStream() { + { + // Stop and close in case not already closed. + std::lock_guard lock(mLock); + if (mStream) { + mStream->stop(); + mStream->close(); + } + } + return start(); + } + + oboe::Result start() { + std::lock_guard lock(mLock); + auto result = createPlaybackStream(); + if(result == oboe::Result::OK) { + mCallback->setSource(std::dynamic_pointer_cast(mAudioSource)); + mStream->start(); + mIsLatencyDetectionSupported = (mStream->getTimestamp((CLOCK_MONOTONIC)) != oboe::Result::ErrorUnimplemented); + mStatus = RUNNING; + LOGD("EUPHONY / EpnyTxEngine: %s", oboe::convertToText(result)); + } else { + mStatus = STOP; + LOGE("Error creating playback stream. Error: %s", oboe::convertToText(result)); + } + + return result; + } + + void setCode(std::string data) { + txPacket = Packet::create() + .setPayloadWithASCII(std::move(data)) + .basedOnBase16() + .build(); + + txPacket->setBaseType(mBaseCodingType); + + if (mAudioSource != nullptr) { + auto waveList = mModem->modulate(txPacket->toString()); + std::dynamic_pointer_cast(mAudioSource)->setWaveList(waveList); + } + } + + void setCodingType(int codingTypeSrc) { + switch(codingTypeSrc) { + case 0: + mBaseCodingType = BaseType::BASE2; + break; + default: + case 1: + mBaseCodingType = BaseType::BASE16; + break; + } + + if(txPacket != nullptr) { + txPacket->setBaseType(mBaseCodingType); + } + } + + void setMode(int modeSrc) { + switch(modeSrc) { + case 0: + default: + mModeType = ModeType::DEFAULT; + break; + case 1: + mModeType = ModeType::EUPI; + break; + } + + mAudioSource = createAudioSource(mModeType); + } + + void setModulation(int modulationTypeSrc) { + switch(modulationTypeSrc) { + case 0: + default: + mModulationType = ModulationType::FSK; + break; + } + + mModem = ModemFactory::create(mModulationType); + } + + void setModulation(ModulationType modulationTypeSrc) { + switch(modulationTypeSrc) { + case ModulationType::FSK: + default: + mModulationType = ModulationType::FSK; + break; + } + + mModem = ModemFactory::create(mModulationType); + } + + void setEupiFrequency(double freq) { + eupiFreq = freq; + } + + void setBufferSizeInBursts(int32_t numBursts) + { + std::lock_guard lock(mLock); + if (!mStream) return; + + mIsLatencyDetectionSupported = false; + mCallback->setBufferTuneEnabled(numBursts == kBufferSizeAutomatic); + auto result = mStream->setBufferSizeInFrames( + numBursts * mStream->getFramesPerBurst()); + if (result) { + LOGD("Buffer size successfully changed to %d", result.value()); + } else { + LOGW("Buffer size could not be changed, %d", result.error()); + } + } + + double getCurrentOutputLatencyMillis() { + if (!mIsLatencyDetectionSupported) return -1.0; + + std::lock_guard lock(mLock); + if (!mStream) return -1.0; + + // Get the time that a known audio frame was presented for playing + auto result = mStream->getTimestamp(CLOCK_MONOTONIC); + double outputLatencyMillis = -1; + const int64_t kNanosPerMillisecond = 1000000; + if (result == oboe::Result::OK) { + oboe::FrameTimestamp playedFrame = result.value(); + // Get the write index for the next audio frame + int64_t writeIndex = mStream->getFramesWritten(); + // Calculate the number of frames between our known frame and the write index + int64_t frameIndexDelta = writeIndex - playedFrame.position; + // Calculate the time which the next frame will be presented + int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / (mStream->getSampleRate()); + int64_t nextFramePresentationTime = playedFrame.timestamp + frameTimeDelta; + // Assume that the next frame will be written at the current time + using namespace std::chrono; + int64_t nextFrameWriteTime = + duration_cast(steady_clock::now().time_since_epoch()).count(); + // Calculate the latency + outputLatencyMillis = static_cast(nextFramePresentationTime - nextFrameWriteTime) + / kNanosPerMillisecond; + } else { + LOGE("Error calculating latency: %s", oboe::convertToText(result.error())); + } + + return outputLatencyMillis; + } + + bool isLatencyDetectionSupported() const { + return mIsLatencyDetectionSupported; + } +}; + +TxEngine::TxEngine() +: pImpl(std::make_unique()) { } + + +TxEngine::~TxEngine() = default; + + +void TxEngine::tap(bool isDown) { + if(pImpl->mAudioSource != nullptr) + pImpl->mAudioSource->tap(isDown); +} + +void TxEngine::tapCount(bool isDown, int count) { + if(pImpl->mAudioSource != nullptr) + std::dynamic_pointer_cast(pImpl->mAudioSource)->tapCount(isDown, count); +} + +void TxEngine::setEupiFrequency(double freq) { + pImpl->setEupiFrequency(freq); + + if(pImpl->mAudioSource != nullptr) + std::dynamic_pointer_cast(pImpl->mAudioSource)->setFrequency(freq); +} + +void TxEngine::stop() { + pImpl->stop(); +} + +void TxEngine::start() { + pImpl->start(); +} + +void TxEngine::setCode(std::string data) { + pImpl->setCode(std::move(data)); +} + +void TxEngine::setCodingType(int codingTypeSrc) { + pImpl->setModulation(codingTypeSrc); +} + +void TxEngine::setMode(int modeSrc) { + pImpl->setMode(modeSrc); +} + +void TxEngine::setModulation(int modulationTypeSrc) { + pImpl->setModulation(modulationTypeSrc); +} + +bool TxEngine::isLatencyDetectionSupported() { + return pImpl->isLatencyDetectionSupported(); +} + +void TxEngine::setAudioApi(oboe::AudioApi audioApi) { + pImpl->mAudioApi = audioApi; +} + +void TxEngine::setPerformance(oboe::PerformanceMode mode) { + pImpl->setPerformance(mode); +} + +void TxEngine::setChannelCount(int channelCount) { + pImpl->mChannelCount = channelCount; +} + +void TxEngine::setDeviceId(int32_t deviceId) { + pImpl->mDeviceId = deviceId; +} + +int TxEngine::getFramesPerBursts() { + return pImpl->mStream->getFramesPerBurst(); +} + +void TxEngine::setBufferSizeInBursts(int32_t numBursts) { + pImpl->setBufferSizeInBursts(numBursts); +} + +double TxEngine::getCurrentOutputLatencyMillis() { + return pImpl->getCurrentOutputLatencyMillis(); +} + +Status Euphony::TxEngine::getStatus() { + return pImpl->mStatus; +} + +std::string TxEngine::getCode() { + return pImpl->txPacket->getPayloadStr(); +} + +std::string TxEngine::getGenCode() { + return pImpl->txPacket->toString(); +} + diff --git a/euphony/src/main/cpp/core/source/WaveRenderer.cpp b/euphony/src/main/cpp/core/source/WaveRenderer.cpp new file mode 100644 index 0000000..870a8f4 --- /dev/null +++ b/euphony/src/main/cpp/core/source/WaveRenderer.cpp @@ -0,0 +1,82 @@ +#include + +#include "../WaveRenderer.h" + +using namespace Euphony; + +WaveRenderer::WaveRenderer(WaveList waveListSrc, int32_t channelCountSrc) +: channelCount (channelCountSrc) +, waveSourceSize(0) +, readFrameIndex(0) +, renderIndex(0) +, renderTotalCount(0) +{ + setWaveList(std::move(waveListSrc)); +} + +void WaveRenderer::renderAudio(float *targetData, int32_t numFrames) { + if(isWaveOn) { + int64_t framesToRenderFromData = numFrames; + const float *waveSrcData = waveSource.get(); + + if (readFrameIndex + numFrames >= waveSourceSize) { + framesToRenderFromData = waveSourceSize - readFrameIndex; + } + + for (int i = 0; i < framesToRenderFromData; ++i) { + for (int j = 0; j < channelCount; ++j) { + targetData[(i * channelCount) + j] = waveSrcData[readFrameIndex]; + } + if (++readFrameIndex >= waveSourceSize){ + readFrameIndex = 0; + if(renderTotalCount > 0) { + if(++renderIndex >= renderTotalCount) + isWaveOn.store(false); + } + } + } + + if(framesToRenderFromData < numFrames){ + renderSilence(&targetData[framesToRenderFromData], numFrames * channelCount); + } + + } else { + renderSilence(targetData, numFrames * channelCount); + } +} + +void WaveRenderer::tap(bool isDown) { + isWaveOn.store(isDown); +} + +void WaveRenderer::tapCount(bool isDown, int count) { + isWaveOn.store(isDown); + renderIndex = 0; + renderTotalCount = count; +} + +float* WaveRenderer::getWaveSource() { + return waveSource.get(); +} + +int32_t WaveRenderer::getWaveSourceSize() const { + return waveSourceSize; +} + +void WaveRenderer::renderSilence(float *targetData, int32_t numFrames) { + for(int i = 0; i < numFrames; ++i) { + targetData[i] = 0; + } +} + +void WaveRenderer::setWaveList(WaveList waveListSrc) { + waveSourceSize = waveListSrc.size() * kBufferSize; + waveSource = std::make_unique(waveSourceSize); + std::fill_n(waveSource.get(), waveSourceSize, 0); + for(int i = 0; i < waveListSrc.size(); i++) { + auto waveSrc = waveListSrc[i]->getSource(); + for(int j = 0; j < kBufferSize; j++) { + waveSource[j + (i * kBufferSize)] = waveSrc[j]; + } + } +} diff --git a/euphony/src/main/cpp/native-connector.cpp b/euphony/src/main/cpp/native-connector.cpp new file mode 100644 index 0000000..cf3f921 --- /dev/null +++ b/euphony/src/main/cpp/native-connector.cpp @@ -0,0 +1,305 @@ +// +// Created by opener on 20. 8. 25. +// + +#include +#include +#include "debug-helper/Log.h" +#include "core/TxEngine.h" + +using namespace Euphony; + +extern "C" { + JNIEXPORT jlong JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1createEngine(JNIEnv *env, jobject thiz) { + auto engine = new(std::nothrow) TxEngine(); + return reinterpret_cast(engine); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1deleteEngine(JNIEnv *env, jobject thiz, + jlong engine_handle) { + delete reinterpret_cast(engine_handle); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setToneOn(JNIEnv *env, jobject thiz, + jlong engine_handle, + jboolean is_tone_on) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->tap(is_tone_on); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setCountToneOn(JNIEnv *env, jobject thiz, jlong engine_handle, + jboolean is_tone_on, jint count) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->tapCount(is_tone_on, count); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setCode(JNIEnv *env, jobject thiz, jlong engine_handle, + jstring data) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + const char* inputData = env->GetStringUTFChars(data, 0); + const std::string inputStr = std::string(inputData); + + engine->setCode(inputStr); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setCodingType(JNIEnv *env, jobject thiz, jlong engine_handle, + jint type) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->setCodingType(type); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setMode(JNIEnv *env, jobject thiz, jlong engine_handle, + jint mode) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->setMode(mode); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setModulation(JNIEnv *env, jobject thiz, jlong engine_handle, + jint modulation_type) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->setModulation(modulation_type); + } + + JNIEXPORT jstring JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1getCode(JNIEnv *env, jobject thiz, jlong engine_handle) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return nullptr; + } + + return env->NewStringUTF(engine->getCode().c_str()); + } + + JNIEXPORT jstring JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1getGenCode(JNIEnv *env, jobject thiz, jlong engine_handle) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return nullptr; + } + + return env->NewStringUTF(engine->getGenCode().c_str()); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1start(JNIEnv *env, jobject thiz, + jlong engine_handle) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->start(); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1stop(JNIEnv *env, jobject thiz, + jlong engine_handle) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->stop(); + } + + JNIEXPORT jint JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1getStatus(JNIEnv *env, jobject thiz, + jlong engine_handle) { + auto engine = reinterpret_cast (engine_handle); + if (engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return 2; + } + + switch(engine->getStatus()) { + case RUNNING: + return 0; + case STOP: + return 1; + } + + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setAudioApi(JNIEnv *env, jobject thiz, + jlong engine_handle, + jint audio_api) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + auto api = static_cast(audio_api); + engine->setAudioApi(api); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setAudioDeviceId(JNIEnv *env, jobject thiz, + jlong engine_handle, + jint device_id) { + auto engine = reinterpret_cast (engine_handle); + if (engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->setDeviceId(device_id); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setAudioFrequency(JNIEnv *env, jobject thiz, + jlong engine_handle, + jdouble frequency) { + auto engine = reinterpret_cast (engine_handle); + if (engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->setEupiFrequency(frequency); + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setChannelCount(JNIEnv *env, jobject thiz, + jlong engine_handle, + jint channel_count) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->setChannelCount(channel_count); + + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setBufferSizeInBursts(JNIEnv *env, jobject thiz, + jlong engine_handle, + jint buffer_size_in_bursts) { + + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + engine->setBufferSizeInBursts(buffer_size_in_bursts); + } + + + JNIEXPORT jdouble JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1getCurrentOutputLatencyMillis(JNIEnv *env, + jobject thiz, + jlong engine_handle) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return static_cast(-1.0); + } + + return static_cast(engine->getCurrentOutputLatencyMillis()); + } + + JNIEXPORT jboolean JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1isLatencyDetectionSupported(JNIEnv *env, + jobject thiz, + jlong engine_handle) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return JNI_FALSE; + } + + return (engine->isLatencyDetectionSupported()) ? JNI_TRUE : JNI_FALSE; + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setPerformance(JNIEnv *env, jobject thiz, + jlong engine_handle, + jint performance_level) { + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return; + } + + switch(performance_level) { + case 0: + engine->setPerformance(oboe::PerformanceMode::PowerSaving); + break; + case 1: + engine->setPerformance(oboe::PerformanceMode::None); + break; + case 2: + default: + engine->setPerformance(oboe::PerformanceMode::LowLatency); + break; + } + } + + JNIEXPORT void JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1setDefaultStreamValues(JNIEnv *env, jobject thiz, + jint sample_rate, + jint frames_per_burst) { + oboe::DefaultStreamValues::SampleRate = (int32_t) sample_rate; + oboe::DefaultStreamValues::FramesPerBurst = (int32_t) frames_per_burst; + } + + JNIEXPORT jint JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1getFramesPerBursts(JNIEnv *env, jobject thiz, + jlong engine_handle) { + + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return -1; + } + + return engine->getFramesPerBursts(); + + } +} \ No newline at end of file diff --git a/euphony/src/main/java/co/euphony/tx/EuTxNativeConnector.java b/euphony/src/main/java/co/euphony/tx/EuTxNativeConnector.java new file mode 100644 index 0000000..d3cae2c --- /dev/null +++ b/euphony/src/main/java/co/euphony/tx/EuTxNativeConnector.java @@ -0,0 +1,227 @@ +package co.euphony.tx; + +import android.content.Context; +import android.media.AudioManager; +import android.os.Build; +import android.util.Log; + +import co.euphony.util.EuOption; + +public class EuTxNativeConnector { + long mEngineHandle = 0; + + public enum EpnyStatus { + RUNNING, STOP, NO_CREATE + } + + public enum EpnyPerformanceMode { + PowerSavingMode, + NormalMode, + SuperPowerMode + } + + static { + System.loadLibrary("euphony"); + } + + public EuTxNativeConnector() { + if(!create()) { + Log.e("EUPHONY_ERROR","Euphony Engine Creation was failed."); + } else { + Log.d("EUPHONY_MSG","Euphony Engine Creation was successful"); + } + } + + public EuTxNativeConnector(Context context) { + if(!create(context)){ + Log.e("EUPHONY_ERROR","Euphony Engine Creation was failed."); + } else { + Log.d("EUPHONY_MSG","Euphony Engine Creation was successful"); + } + } + + public static EuTxNativeConnector newInstance() { + return new EuTxNativeConnector(); + } + + boolean create() { + if(mEngineHandle == 0) + mEngineHandle = native_createEngine(); + + return (mEngineHandle != 0); + } + + boolean create(Context context) { + if(mEngineHandle == 0) { + setDefaultStreamValues(context); + mEngineHandle = native_createEngine(); + } + + return (mEngineHandle != 0); + } + + void clean() { + if(mEngineHandle != 0) + native_deleteEngine(mEngineHandle); + + mEngineHandle = 0; + } + + public void start() { + if(mEngineHandle != 0) + native_start(mEngineHandle); + } + + public void stop() { + if(mEngineHandle != 0) + native_stop(mEngineHandle); + } + + public void setPerformance(EpnyPerformanceMode mode, Context context) { + if(mEngineHandle != 0) { + switch(mode) { + case PowerSavingMode: + native_setPerformance(mEngineHandle, 0); + break; + case NormalMode: + native_setPerformance(mEngineHandle, 1); + break; + case SuperPowerMode: + native_setPerformance(mEngineHandle, 2); + break; + } + } + } + + public void setCodingType(EuOption.CodingType codingType) { + if(mEngineHandle != 0) { + native_setCodingType(mEngineHandle, codingType.ordinal()); + } + } + + public void setMode(EuOption.ModeType modeType) { + if(mEngineHandle != 0) { + native_setMode(mEngineHandle, modeType.ordinal()); + } + } + + public void setModulation(EuOption.ModulationType modulationType) { + if(mEngineHandle != 0) { + native_setModulation(mEngineHandle, modulationType.ordinal()); + } + } + + private void setDefaultStreamValues(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); + Log.d("EUPHONY_MSG","This device's samplerate for output : " + sampleRateStr); + int defaultSampleRate = Integer.parseInt(sampleRateStr); + if(defaultSampleRate == 0) defaultSampleRate = 44100; + + String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); + Log.d("EUPHONY_MSG","This device's frames per buffer for output : " + framesPerBurstStr); + int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr); + if(defaultFramesPerBurst == 0) defaultFramesPerBurst = 256; // Use Default + native_setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst); + } + } + private void setDefaultStreamValues(int sampleRate, int framesPerBurst) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){ + native_setDefaultStreamValues(sampleRate, framesPerBurst); + } + } + + public void setToneOn(boolean isToneOn) { + if(mEngineHandle != 0) native_setToneOn(mEngineHandle, isToneOn); + } + + public void setCountToneOn(boolean isToneOn, int count) { + if(mEngineHandle != 0) native_setCountToneOn(mEngineHandle, isToneOn, count); + } + + public void setCode(String data) { + if(mEngineHandle != 0) native_setCode(mEngineHandle, data); + } + + public String getCode() { + if(mEngineHandle != 0) + return native_getCode(mEngineHandle); + return null; + } + + public String getGenCode() { + if(mEngineHandle != 0) + return native_getGenCode(mEngineHandle); + return null; + } + + public void setAudioFrequency(double freq) { + if(mEngineHandle != 0) native_setAudioFrequency(mEngineHandle, freq); + } + + public void setAudioApi(int audioApi){ + if (mEngineHandle != 0) native_setAudioApi(mEngineHandle, audioApi); + } + + public void setAudioDeviceId(int deviceId){ + if (mEngineHandle != 0) native_setAudioDeviceId(mEngineHandle, deviceId); + } + + public void setChannelCount(int channelCount) { + if (mEngineHandle != 0) native_setChannelCount(mEngineHandle, channelCount); + } + + public int getFramesPerBursts(){ + if (mEngineHandle != 0) return native_getFramesPerBursts(mEngineHandle); + else return -1; + } + + public void setBufferSizeInBursts(int bufferSizeInBursts){ + if (mEngineHandle != 0) native_setBufferSizeInBursts(mEngineHandle, bufferSizeInBursts); + } + + public double getCurrentOutputLatencyMillis(){ + if (mEngineHandle == 0) return 0; + return native_getCurrentOutputLatencyMillis(mEngineHandle); + } + + public boolean isLatencyDetectionSupported() { + return mEngineHandle != 0 && native_isLatencyDetectionSupported(mEngineHandle); + } + + public EpnyStatus getStatus() { + if(mEngineHandle == 0) return EpnyStatus.NO_CREATE; + switch(native_getStatus(mEngineHandle)) { + case 0: + return EpnyStatus.RUNNING; + case 1: + return EpnyStatus.STOP; + } + return EpnyStatus.NO_CREATE; + } + + private native long native_createEngine(); + private native void native_deleteEngine(long engineHandle); + private native void native_start(long engineHandle); + private native void native_stop(long engineHandle); + private native int native_getStatus(long engineHandle); + private native void native_setPerformance(long engineHandle, int performanceLevel); + private native void native_setToneOn(long engineHandle, boolean isToneOn); + private native void native_setCountToneOn(long engineHandle, boolean isToneOn, int count); + private native void native_setCode(long engineHandle, String data); + private native void native_setCodingType(long engineHandle, int type); + private native void native_setMode(long engineHandle, int mode); + private native void native_setModulation(long engineHandle, int modulationType); + private native String native_getCode(long engineHandle); + private native String native_getGenCode(long engineHandle); + private native void native_setAudioFrequency(long engineHandle, double frequency); + private native void native_setAudioApi(long engineHandle, int audioApi); + private native void native_setAudioDeviceId(long engineHandle, int deviceId); + private native void native_setChannelCount(long engineHandle, int channelCount); + private native int native_getFramesPerBursts(long engineHandle); + private native void native_setBufferSizeInBursts(long engineHandle, int bufferSizeInBursts); + private native double native_getCurrentOutputLatencyMillis(long engineHandle); + private native boolean native_isLatencyDetectionSupported(long engineHandle); + private native void native_setDefaultStreamValues(int sampleRate, int framesPerBurst); +} From 0bace6cf985482aca0b07bfd7b0d471c472f16cc Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 08:44:20 +0900 Subject: [PATCH 18/31] Replaced Tx with euphony-native-inteface for faster processing. > Removed Tx functions based on Java > Connected Tx with euphony-native-interface --- .../main/java/co/euphony/tx/EuCodeMaker.java | 120 ------------- .../java/co/euphony/tx/EuDataEncoder.java | 70 -------- .../java/co/euphony/tx/EuFreqGenerator.java | 167 ------------------ .../src/main/java/co/euphony/tx/EuPlayer.java | 41 ----- .../main/java/co/euphony/tx/EuTxManager.java | 117 ++++++------ .../lib/transmitter/TransmitterUnitTest.java | 3 - 6 files changed, 62 insertions(+), 456 deletions(-) delete mode 100644 euphony/src/main/java/co/euphony/tx/EuCodeMaker.java delete mode 100644 euphony/src/main/java/co/euphony/tx/EuDataEncoder.java delete mode 100644 euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java delete mode 100644 euphony/src/main/java/co/euphony/tx/EuPlayer.java diff --git a/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java b/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java deleted file mode 100644 index b9b8d96..0000000 --- a/euphony/src/main/java/co/euphony/tx/EuCodeMaker.java +++ /dev/null @@ -1,120 +0,0 @@ -package co.euphony.tx; - -import android.util.Log; -import co.euphony.util.PacketErrorDetector; - -public class EuCodeMaker extends EuFreqGenerator { - String mMainString; - short[] mCodeSource; - public int START_BIT = getFreqBasePoint() - getFreqSpan(); - - public static enum CHANNEL { - SINGLE, MULTI, EXINGLE - } - - private CHANNEL mChannelMode = CHANNEL.EXINGLE; - - public EuCodeMaker() - { } - - public EuCodeMaker(CHANNEL channelMode) - { - mChannelMode = channelMode; - } - - - public short[] assembleData(String data) - { - short[] assembledData = applyCrossFade(makeStaticFrequency(START_BIT, 0)); - int[] payload = new int[data.length() + 1]; - int payloadSum = 0; - - switch(mChannelMode) - { - case SINGLE: - for(int i = 0; i < data.length(); i++) - { - switch(data.charAt(i)) - { - case '0': - assembledData = appendRawData(assembledData, getZeroSource()); - break; - case '1': - assembledData = appendRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint(),0))); - break; - } - } - break; - - case MULTI: - for(int i = 0; i < data.length(); i++) - { - switch(data.charAt(i)) - { - case '0': - break; - case '1': - assembledData = mixingRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan()*(i+1), 0))); - break; - } - } - break; - - case EXINGLE: - for(int i = 0; i < data.length(); i++){ - char ch = data.charAt(i); - switch(ch) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - assembledData = appendRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan() * (ch - '0'), 0))); - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - assembledData = appendRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan() * (ch - 'a' + 10), 0))); - break; - } - } - break; - } - - for(int i = 0; i < data.length(); i++){ - switch(data.charAt(i)) - { - case '0': case '1': - case '2': case '3': - case '4': case '5': - case '6': case '7': - case '8': case '9': - payload[i] = data.charAt(i) - '0'; - break; - case 'a': case 'b': - case 'c': case 'd': - case 'e': case 'f': - payload[i] = (data.charAt(i) - 'a') + 10; - break; - } - payloadSum += payload[i]; - } - int checksum = PacketErrorDetector.makeCheckSum(payloadSum); - int parity = PacketErrorDetector.makeParellelParity(payload); - - Log.i("euphony_code", "CODE" + data + "CHECKSUM : " + checksum + " PARITY : " + parity); - assembledData = appendRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan() * checksum, 0))); - assembledData = appendRawData(assembledData, applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan() * parity, 0))); - - return assembledData; - } -} diff --git a/euphony/src/main/java/co/euphony/tx/EuDataEncoder.java b/euphony/src/main/java/co/euphony/tx/EuDataEncoder.java deleted file mode 100644 index e89a702..0000000 --- a/euphony/src/main/java/co/euphony/tx/EuDataEncoder.java +++ /dev/null @@ -1,70 +0,0 @@ -package co.euphony.tx; - -import co.euphony.util.EuCodec; - -public class EuDataEncoder extends EuCodec { - private String mOriginalSource; - - EuDataEncoder() { - } - - public EuDataEncoder(String _source) { - mOriginalSource = _source; - } - - public String encodeHexCharSource() { - return encodeStaticHexCharSource(mOriginalSource); - } - - public static String encodeStaticHexCharSource(String _source) { - StringBuilder strBuilder = new StringBuilder(); - - for (int i = 0; i < _source.length(); i++) { - int data = _source.charAt(i); - strBuilder.append(Integer.toHexString(data)); - } - - return strBuilder.toString(); - } - - /* - TODO: 40 BASE should be implemented. - public static byte[] base40StaticEncode(String _source) - { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - for(int i = 0; i < _source.length(); i+=3) - { - switch(Math.min(3, _source.length() - i)) { - case 1: - byte b = base40Index[_source.charAt(i)]; - dos.writeByte(b); - break; - case 2: - char ch = (char) ((base40Index[_source.charAt(i+1)]) * 40 + base40Index[_source.charAt(i)]); - dos.writeChar(ch); - break; - case 3: - char ch2 = (char) ((char) ((base40Index[_source.charAt(i+2)]) * 40 + base40Index[_source.charAt(i+1)]) * 40 + base40Index[_source.charAt(i)]); - dos.writeChar(ch2); - - break; - } - } - return baos.toByteArray(); - } - catch(IOException e) - { - throw new AssertionError(e); - } - } - */ - - public String getOriginalSource() { - return mOriginalSource; - } - public void setOriginalSource(String mOriginalSource) { - this.mOriginalSource = mOriginalSource; - } -} diff --git a/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java b/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java deleted file mode 100644 index 8d429d1..0000000 --- a/euphony/src/main/java/co/euphony/tx/EuFreqGenerator.java +++ /dev/null @@ -1,167 +0,0 @@ -package co.euphony.tx; - -import co.euphony.common.Constants; - -public class EuFreqGenerator { - - // FIXED ACOUSTIC DATA - public final int SAMPLERATE = Constants.SAMPLERATE;//44100; - public final int DATA_LENGTH = Constants.DATA_LENGTH;//2048; - public final double PI = Math.PI; - public final double PI2 = PI * 2; - - // Member for Frequency point - // DEFAULT DEFINITION - private int mFreqBasePoint = Constants.START_FREQ; - private int mFreqSpan = Constants.CHANNEL_SPAN; //86 - private short[] mZeroSource = new short[DATA_LENGTH]; - - public EuFreqGenerator() { } - - public EuFreqGenerator(int freqStartPoint, int freqSpan) - { - mFreqSpan = freqSpan; - mFreqBasePoint = freqStartPoint; - } - - public short[] makeStaticFrequency(int freq, int degree) - { - double[] double_source = new double[DATA_LENGTH]; - short[] source = new short[DATA_LENGTH]; - double time, phase; - - for(int i = 0; i < DATA_LENGTH; i++) - { - time = (double)i / (double)SAMPLERATE; - double_source[i] = Math.sin(PI2 * (double)freq * time); - source[i] = (short)(32767 * double_source[i]); - } - - return source; - } - - public void euMakeFrequency(short[] source, int freq) - { - source = makeStaticFrequency(freq, 0); - } - - public short[] makeFrequencyWithCrossFade(int freq) - { - return applyCrossFade(makeStaticFrequency(freq, 0)); - } - - public short[] makeFrequencyWithValue(int value) { - return applyCrossFade(makeStaticFrequency(getFreqBasePoint() + getFreqSpan() * value, 0)); - } - - public short[] applyCrossFade(short[] source) - { - double mini_window; - int fade_section = Constants.FADE_RANGE; - for(int i = 0; i < fade_section; i++) - { - mini_window = (double)i / (double)fade_section; - source[i] *= mini_window; - source[DATA_LENGTH-1-i] *= mini_window; - } - - return source; - } - - public short[] appendRawData(short[] src, short[] objective) - { - int SRC_LENGTH, TOTAL_LENGTH; - if(src == null) - SRC_LENGTH = 0; - else - SRC_LENGTH = src.length; - TOTAL_LENGTH = SRC_LENGTH + objective.length; - - short[] dest = new short[TOTAL_LENGTH]; - - for(int i = 0; i < SRC_LENGTH; i++) - dest[i] = src[i]; - for(int i = SRC_LENGTH; i < TOTAL_LENGTH; i++) - dest[i] = objective[i - SRC_LENGTH]; - - return dest; - } - - public short[] euLinkRawData(short[]... sources) - { - short[] dest = new short[sources.length * DATA_LENGTH]; - - for(int i = 0; i < sources.length; i++) - for(int j = 0; j < sources[i].length; j++) - dest[j + i * DATA_LENGTH] = sources[i][j]; - - return dest; - } - - public short[] euLinkRawData(boolean isCrossfaded, short[]... sources) - { - short[] dest = new short[sources.length * DATA_LENGTH]; - for(int i = 0; i < sources.length; i++) - { - if(isCrossfaded) - sources[i] = applyCrossFade(sources[i]); - - for(int j = 0; j < sources[i].length; j++) - dest[j + i * DATA_LENGTH] = sources[i][j]; - } - - return dest; - } - - public short[] mixingRawData(short[]... sources) - { - short[] dest = sources[0].clone(); - - for(int i = 1; i < sources.length; i++) - { - for(int j = 0; j < sources[i].length; j++){ - dest[j] = (short) (((int)dest[j] + (int)sources[i][j])/2); - } - } - return dest; - } - - public short[] euMakeMaximumVolume(short[] source) - { - int max = 0; - //SCAN FOR VOLUME UP - for (short i1 : source) - if (max < Math.abs(i1)) - max = i1; - if(32767 == max) - return source; - - for(int i = 0; i < source.length; i++) - source[i] *= 32767/max; - - return source; - } - - public short[] getZeroSource() { - return mZeroSource; - } - - public int getFreqBasePoint() { - return mFreqBasePoint; - } - - public void setFreqBasePoint(int mFreqBasePoint) { - this.mFreqBasePoint = mFreqBasePoint; - } - - public int getFreqSpan() { - return mFreqSpan; - } - - public void setFreqSpan(int mFreqSpan) { - this.mFreqSpan = mFreqSpan; - } - - -} - diff --git a/euphony/src/main/java/co/euphony/tx/EuPlayer.java b/euphony/src/main/java/co/euphony/tx/EuPlayer.java deleted file mode 100644 index fe2fa80..0000000 --- a/euphony/src/main/java/co/euphony/tx/EuPlayer.java +++ /dev/null @@ -1,41 +0,0 @@ -package co.euphony.tx; - -import co.euphony.common.Constants; -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioTrack; - -public class EuPlayer { - - private short[] mSource; - private AudioTrack mAudioTrack = null; - private final int DATA_LENGTH = Constants.FFT_SIZE * 4;//2048; - private short[] mZeroSource = new short[DATA_LENGTH]; - - public EuPlayer() { } - - public EuPlayer(short[] src) - { - this.setSource(src); - } - - public void setSource(short[] src) - { - mSource = src; - mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, src.length*2, AudioTrack.MODE_STATIC); - } - - public void Play() - { - mAudioTrack.write(mSource, 0, mSource.length); - mAudioTrack.setLoopPoints(0, mSource.length, -1); - mAudioTrack.play(); - } - - public void Stop() - { - if(mAudioTrack != null) - if(mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) - mAudioTrack.pause(); - } -} diff --git a/euphony/src/main/java/co/euphony/tx/EuTxManager.java b/euphony/src/main/java/co/euphony/tx/EuTxManager.java index a37041e..fd8e89d 100644 --- a/euphony/src/main/java/co/euphony/tx/EuTxManager.java +++ b/euphony/src/main/java/co/euphony/tx/EuTxManager.java @@ -1,25 +1,24 @@ package co.euphony.tx; import android.content.Context; -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioTrack; -import android.util.Log; +import android.os.Handler; +import android.os.Looper; import co.euphony.util.EuOption; public class EuTxManager { - private AudioTrack mAudioTrack = null; - private EuCodeMaker mCodeMaker = new EuCodeMaker(); - private EuDataEncoder mDataEncoder = new EuDataEncoder(); + private EuTxNativeConnector txCore; - public short[] getOutStream() { - return mOutStream; + public enum EuPIDuration { + LENGTH_SHORT, + LENGTH_LONG, + LENGTH_FOREVER + } + + public EuTxManager(Context context) { + txCore = new EuTxNativeConnector(context); } - private short[] mOutStream; - - public EuTxManager() { } /* * @deprecated Replaced by {@link #setCode()}, deprecated for naming & dynamic option. */ @@ -30,57 +29,65 @@ public void euInitTransmit(String data) { public void setCode(String data) { - String code = data; - code = mDataEncoder.encodeStaticHexCharSource(data); - mOutStream = mCodeMaker.assembleData(code); + txCore.setCode(data); } - public void process() { process(1); } + public String getCode() { + return txCore.getCode(); + } - public void process(int count) - { - if(count > 0) - mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, mOutStream.length*2, AudioTrack.MODE_STREAM); - else { - mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, mOutStream.length*2, AudioTrack.MODE_STATIC); - count = -1; - } + public void callEuPI(double freq, EuPIDuration duration) { + txCore.setMode(EuOption.ModeType.EUPI); + txCore.setToneOn(true); + txCore.setAudioFrequency(freq); + txCore.start(); - mAudioTrack.setLoopPoints(0, mOutStream.length, count); - - if(mAudioTrack != null){ - try{ - mAudioTrack.write(mOutStream, 0, mOutStream.length); - mAudioTrack.play(); - } - catch(IllegalStateException e) - { - Log.i("PROCESS", e.getMessage()); - } + if (duration != EuPIDuration.LENGTH_FOREVER) { + new Handler(Looper.getMainLooper()).postDelayed(this::stop, + (duration == EuPIDuration.LENGTH_SHORT) ? 200 : 500); } } - - public void stop() - { - if(mAudioTrack != null) - mAudioTrack.pause(); + + public String getGenCode() { + return txCore.getGenCode(); } - - public void setSoftVolume(float ratio) - { - for(int i = 0; i < mOutStream.length; i++) - mOutStream[i] *= ratio; + + public short[] getOutStream() { + + /* TODO: legacy code will be removed. + That will be bring from EuphonyTx. + return mOutStream; + */ + return null; } - public void setSystemVolumeMax(Context _context) + public void play() { + play(1); + } + + public void play(int count) { + txCore.setMode(EuOption.ModeType.DEFAULT); + txCore.setCountToneOn(true, count); + txCore.start(); + } + + /* + * @deprecated Replaced by {@link #setCode()}, deprecated for naming issue + */ + @Deprecated + public void process() { play(1); } + + /* + * @deprecated Replaced by {@link #setCode()}, deprecated for naming issue + */ + @Deprecated + public void process(int count) { play(count); } + + public void stop() { - AudioManager am = (AudioManager) _context.getSystemService(Context.AUDIO_SERVICE); - - if (am != null) { - am.setStreamVolume( - AudioManager.STREAM_MUSIC, - am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), - 0); - } - } + txCore.setToneOn(false); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + txCore.stop(); + },300); + } } diff --git a/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java b/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java index a42b253..97387f5 100644 --- a/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java +++ b/euphony/src/test/java/euphony/lib/transmitter/TransmitterUnitTest.java @@ -2,9 +2,6 @@ import org.junit.Ignore; import org.junit.Test; -import co.euphony.tx.EuDataEncoder; -import co.euphony.tx.EuTxManager; -import co.euphony.util.EuOption; import static org.junit.Assert.assertEquals; public class TransmitterUnitTest { From 85c2b53112529fea396436449f11301b80282e29 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 10:01:21 +0900 Subject: [PATCH 19/31] Added Oboe Library dependency on Unit-test's CMakeLists --- euphony/src/main/cpp/tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index ba8d32d..f870510 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -30,6 +30,7 @@ set(TARGET_TEST_DIR /data/local/tmp/${TEST_EUPHONY}) # Directory on device to pu set(TARGET_TEST_LIB_DIR ${TARGET_TEST_DIR}/${ANDROID_ABI}) set(LIBCPP_SHARED_PATH ${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/${ANDROID_ABI}/libc++_shared.so) +get_target_property( OBOE_LIBRARY_PATH oboe::oboe IMPORTED_LOCATION) find_program(ADB NAMES adb PATHS ${ANDROID_SDK_ROOT}/platform-tools) # Verified to be working on Linux. execute_process(COMMAND ${ADB} shell getprop ro.product.cpu.abi @@ -47,6 +48,7 @@ else() # Push necessary libraries COMMAND ${ADB} push $ ${TARGET_TEST_LIB_DIR}/ + COMMAND ${ADB} push ${OBOE_LIBRARY_PATH} ${TARGET_TEST_LIB_DIR}/ COMMAND ${ADB} push ${LIBCPP_SHARED_PATH} ${TARGET_TEST_LIB_DIR}/ # Push Euphony Test Binary From c0a767b22f45bba2f92f0969b56eb064ee33e586 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 10:02:10 +0900 Subject: [PATCH 20/31] Added EuTxManager's Instrumental Test --- .../java/co/euphony/tx/EuTxManagerTest.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 euphony/src/androidTest/java/co/euphony/tx/EuTxManagerTest.java diff --git a/euphony/src/androidTest/java/co/euphony/tx/EuTxManagerTest.java b/euphony/src/androidTest/java/co/euphony/tx/EuTxManagerTest.java new file mode 100644 index 0000000..852820c --- /dev/null +++ b/euphony/src/androidTest/java/co/euphony/tx/EuTxManagerTest.java @@ -0,0 +1,88 @@ +package co.euphony.tx; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; + +import static org.junit.Assert.*; + +@RunWith(Parameterized.class) +@SmallTest +public class EuTxManagerTest { + private static final String TX_TAG = "EU_TXMANAGER_TAG"; + + String code; + String expectedASCIICode; + String expectedGenCode; + int expectedStreamLength; + int expectedBufferLength; + + EuTxManager txManager = null; + public EuTxManagerTest(String code, String asciiCode, String expectedGenCode, int expectedStreamLength, int expectedBufferLength) { + this.code = code; + this.expectedASCIICode = asciiCode; + this.expectedGenCode = expectedGenCode; + this.expectedStreamLength = expectedStreamLength; + this.expectedBufferLength = expectedBufferLength; + } + + @Before + public void setup() { + txManager = new EuTxManager(ApplicationProvider.getApplicationContext()); + } + + @Test + public void getCode() { + txManager.setCode(code); + + String activeResult = txManager.getCode(); + assertEquals(expectedASCIICode, activeResult); + } + + @Test + public void getGenCode() { + txManager.setCode(code); + + String activeResult = txManager.getGenCode(); + assertEquals(expectedGenCode, activeResult); + } + + @Test + public void setGetCodeTest() { + txManager.setCode("a"); + String activeResult = txManager.getGenCode(); + assertEquals("S6197", activeResult); + + txManager.setCode("b"); + activeResult = txManager.getGenCode(); + assertEquals("S6284", activeResult); + + } + + @Test + public void testRun() { + txManager.callEuPI(18000, EuTxManager.EuPIDuration.LENGTH_SHORT); + } + + @Parameterized.Parameters + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {"a", "61", "S6197", 5*2048, 5*8}, + {"b", "62", "S6284", 5*2048, 5*8}, + {"c", "63", "S6375", 5*2048, 5*8}, + {"abc", "616263", "S61626386", 9*2048, 9*8}, + {"Abc", "416263", "S416263a4", 9*2048, 9*8}, + {"lmno", "6c6d6e6f", "S6c6d6e6f20", 11*2048, 11*8}, + {"efg", "656667", "S656667c2", 9*2048, 9*8}, + {"abcdefghijklmnopqrstuvwxyz", "6162636465666768696a6b6c6d6e6f707172737475767778797a", "S6162636465666768696a6b6c6d6e6f707172737475767778797aaa", 55*2048, 55*8}, + {"ABC", "414243", "S414243e4", 9*2048, 9*8}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "4142434445464748494a4b4c4d4e4f505152535455565758595a", "S4142434445464748494a4b4c4d4e4f505152535455565758595aea", 55*2048, 55*8}, + }); + } +} \ No newline at end of file From ac81a799e6af91cbe0afc8c19a94459b17ae67b6 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 10:05:11 +0900 Subject: [PATCH 21/31] Added EuPI concept on Rx > EuPI is the programming interface for Euphony > It is like a mouse or keyboard. Wave is also input method. > EuPI provides KEY_DOWN, KEY_UP, KEY_PRESSED listeners --- euphony/src/main/java/co/euphony/rx/EuPI.java | 64 +++++++++++++++++++ .../java/co/euphony/rx/EuPICallDetector.java | 5 ++ 2 files changed, 69 insertions(+) create mode 100644 euphony/src/main/java/co/euphony/rx/EuPI.java create mode 100644 euphony/src/main/java/co/euphony/rx/EuPICallDetector.java diff --git a/euphony/src/main/java/co/euphony/rx/EuPI.java b/euphony/src/main/java/co/euphony/rx/EuPI.java new file mode 100644 index 0000000..343ad13 --- /dev/null +++ b/euphony/src/main/java/co/euphony/rx/EuPI.java @@ -0,0 +1,64 @@ +package co.euphony.rx; + +public class EuPI { + public enum EuPITrigger { + KEY_DOWN, KEY_UP, KEY_PRESSED + } + + public enum EuPIStatus { + KEY_DOWN, KEY_UP, KEY_PRESSED + } + + private int mKey; + private int mFreqIndex; + EuPITrigger mTrigger; + EuPIStatus mStatus; + EuPICallDetector mAPICallback; + + public EuPI(int key, EuPICallDetector callback) { + mKey = key; + mAPICallback = callback; + mTrigger = EuPITrigger.KEY_PRESSED; + mStatus = EuPIStatus.KEY_UP; + } + + public EuPI(int key, EuPITrigger trigger, EuPICallDetector callback) { + mKey = key; + mAPICallback = callback; + mTrigger = trigger; + mStatus = EuPIStatus.KEY_UP; + } + + public int getKey() { + return mKey; + } + + public void setFreqIndex(int idx) { + mFreqIndex = idx; + } + + public void setTrigger(EuPITrigger trigger) { + mTrigger = trigger; + } + + public EuPITrigger getTrigger() { + return mTrigger; + } + + public void setStatus(EuPIStatus status) { + mStatus = status; + } + + public EuPIStatus getStatus() { + return mStatus; + } + + public int getFreqIndex() { + return mFreqIndex; + } + + public EuPICallDetector getCallback() { + return mAPICallback; + } + +} diff --git a/euphony/src/main/java/co/euphony/rx/EuPICallDetector.java b/euphony/src/main/java/co/euphony/rx/EuPICallDetector.java new file mode 100644 index 0000000..6e68152 --- /dev/null +++ b/euphony/src/main/java/co/euphony/rx/EuPICallDetector.java @@ -0,0 +1,5 @@ +package co.euphony.rx; + +public interface EuPICallDetector { + void call(); +} From c4bab1a7f61bdaa1987f65246c73ab33e614392d Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 10:07:00 +0900 Subject: [PATCH 22/31] Added FrequencyDetector interface > it could detects specific frequency band. --- euphony/src/main/java/co/euphony/rx/FrequencyDetector.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 euphony/src/main/java/co/euphony/rx/FrequencyDetector.java diff --git a/euphony/src/main/java/co/euphony/rx/FrequencyDetector.java b/euphony/src/main/java/co/euphony/rx/FrequencyDetector.java new file mode 100644 index 0000000..cece285 --- /dev/null +++ b/euphony/src/main/java/co/euphony/rx/FrequencyDetector.java @@ -0,0 +1,5 @@ +package co.euphony.rx; + +public interface FrequencyDetector { + void detect(float amplitude); +} From a1ddc0eb6db8be451d0b30e54ff121c3a78cb47b Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 10:13:49 +0900 Subject: [PATCH 23/31] Remove Position Detector > it doesn't no more support. > `Distance positioning` function will be available on v1.0 --- .../main/java/co/euphony/rx/EuRxManager.java | 122 +----------------- .../java/co/euphony/rx/PositionDetector.java | 6 - 2 files changed, 1 insertion(+), 127 deletions(-) delete mode 100644 euphony/src/main/java/co/euphony/rx/PositionDetector.java diff --git a/euphony/src/main/java/co/euphony/rx/EuRxManager.java b/euphony/src/main/java/co/euphony/rx/EuRxManager.java index ad604c5..6df599d 100644 --- a/euphony/src/main/java/co/euphony/rx/EuRxManager.java +++ b/euphony/src/main/java/co/euphony/rx/EuRxManager.java @@ -8,12 +8,9 @@ public class EuRxManager { private Thread mRxThread = null; private RxRunner mRxRunner = null; - private PsRunner mPsRunner = null; - private Thread mPsThread = null; private boolean _active; private static final int RX_DECODE = 1; - private static final int PS_DECODE = 2; private boolean mHex = false; public EuRxManager() { } @@ -31,35 +28,6 @@ public void listen(boolean hex) { mRxThread.start(); } - public void find() - { - _active = true; - mPsRunner = new PsRunner(); - mPsThread = new Thread(mPsRunner, "PS"); - mPsThread.start(); - } - - public void finishToFind() - { - if(mPsThread != null) { - _active = false; - while (true) { - try { - mPsThread.join(); - break; - } catch (InterruptedException e) { - Log.i("FINISH", e.getMessage()); - } - } - } - - if(mPsRunner != null) - mPsRunner.destroyFFT(); - - mPsThread = null; - mPsRunner = null; - } - public void finish() { if(mRxThread != null) { @@ -101,27 +69,7 @@ public void handleMessage(Message msg){ } } }; - - private PositionDetector mPositionDetector; - - public PositionDetector getPositionDetector() { - return mPositionDetector; - } - - public void setPositionDetector(PositionDetector detector) { - this.mPositionDetector = detector; - } - - private Handler mPsHandler = new Handler() { - public void handleMessage(Message msg) { - switch(msg.what){ - case PS_DECODE: - mPositionDetector.detectSignal((Integer)msg.obj); - break; - } - } - }; - + private class RxRunner extends EuFreqObject implements Runnable{ boolean mHex = false; RxRunner() { } @@ -151,72 +99,4 @@ public void run() } } } - - private class PsRunner extends EuFreqObject implements Runnable { - - @Override - public void run() { - boolean startswt = false; - int startcnt = 0; - int specificFreq = 0; - Log.i("START", "START LISTEN"); - while(_active) { - //To find the frequency point - while(!startswt) { - processFFT(); - int i; - for(i = 21000; i >= 16500; i-= Constants.CHANNEL_SPAN) - if(100 < detectFreq(i)){ - startswt = true; - break; - } - specificFreq = i; - - //there is no af area.. - if(startcnt++ > 1000){ - _active = false; - startswt = true; - Log.i("START", "FAILED to find any position"); - } - } - - int signal, max_signal = 0, avr_signal = 0; - int noSignalCnt=0, processingCnt = 0, maxCnt=0; - do{ - processFFT(); - signal = detectFreq(specificFreq); - - if(signal < 20) - noSignalCnt++; - else{ - noSignalCnt = 0; - - if(max_signal < signal){ - maxCnt++; - max_signal = signal; - avr_signal += max_signal; - } - if(++processingCnt > 50){ - avr_signal /= maxCnt; - Message msg = mPsHandler.obtainMessage(); - msg.what = PS_DECODE; - msg.obj = avr_signal; - mPsHandler.sendMessage(msg); - processingCnt = 0; - max_signal = 0; - avr_signal = 0; - maxCnt = 0; - } - } - }while(noSignalCnt < 50 && _active); - - Message msg = mPsHandler.obtainMessage(); - msg.what = PS_DECODE; - msg.obj = -1; - mPsHandler.sendMessage(msg); - break; - - } - } - } } diff --git a/euphony/src/main/java/co/euphony/rx/PositionDetector.java b/euphony/src/main/java/co/euphony/rx/PositionDetector.java deleted file mode 100644 index 086f1c2..0000000 --- a/euphony/src/main/java/co/euphony/rx/PositionDetector.java +++ /dev/null @@ -1,6 +0,0 @@ -package co.euphony.rx; - -public interface PositionDetector { - void detectSignal(int signal); - void detectFq(int fq); -} From 73fcdb2b564615adf796c4e36e7b18d1fe419426 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 19:27:52 +0900 Subject: [PATCH 24/31] Refactoring with FFT --- euphony/src/main/cpp/arms/kiss_fft.c | 127 +++++++++++--------------- euphony/src/main/cpp/arms/kiss_fft.h | 9 +- euphony/src/main/cpp/arms/kiss_fftr.c | 13 ++- 3 files changed, 59 insertions(+), 90 deletions(-) diff --git a/euphony/src/main/cpp/arms/kiss_fft.c b/euphony/src/main/cpp/arms/kiss_fft.c index 0e34159..683e87b 100644 --- a/euphony/src/main/cpp/arms/kiss_fft.c +++ b/euphony/src/main/cpp/arms/kiss_fft.c @@ -4,32 +4,17 @@ fixed or floating point complex numbers. It also delares the kf_ internal functions. */ -static kiss_fft_cpx *scratchbuf=NULL; -static size_t nscratchbuf=0; -static kiss_fft_cpx *tmpbuf=NULL; -static size_t ntmpbuf=0; - -#define CHECKBUF(buf,nbuf,n) \ - do { \ - if ( nbuf < (size_t)(n) ) {\ - free(buf); \ - buf = (kiss_fft_cpx*)KISS_FFT_MALLOC(sizeof(kiss_fft_cpx)*(n)); \ - nbuf = (size_t)(n); \ - } \ - }while(0) - - -static void kf_bfly2( +static +void kf_bfly2( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, int m ) { - kiss_fft_cpx * Fout2; - kiss_fft_cpx * tw1 = st->twiddles; + kiss_fft_cpx *tw1 = st->twiddles; kiss_fft_cpx t; - Fout2 = Fout + m; + kiss_fft_cpx *Fout2 = Fout + m; do{ C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2); @@ -42,7 +27,8 @@ static void kf_bfly2( }while (--m); } -static void kf_bfly4( +static +void kf_bfly4( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, @@ -89,7 +75,8 @@ static void kf_bfly4( }while(--k); } -static void kf_bfly3( +static +void kf_bfly3( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, @@ -133,7 +120,8 @@ static void kf_bfly3( }while(--k); } -static void kf_bfly5( +static +void kf_bfly5( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, @@ -195,7 +183,8 @@ static void kf_bfly5( } /* perform the butterfly for one stage of a mixed radix FFT */ -static void kf_bfly_generic( +static +void kf_bfly_generic( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, @@ -208,12 +197,16 @@ static void kf_bfly_generic( kiss_fft_cpx t; int Norig = st->nfft; - CHECKBUF(scratchbuf,nscratchbuf,p); + kiss_fft_cpx *scratchbuf = (kiss_fft_cpx*) KISS_FFT_MALLOC(sizeof(kiss_fft_cpx) * p); + if(scratchbuf == NULL) { + __android_log_print(ANDROID_LOG_INFO,"KISS_FFT_BFLY_GENERIC","scratch buffer is NULL."); + return; + } for ( u=0; unfft); - //__android_log_print(ANDROID_LOG_INFO,"----","stride1"); - kf_work(tmpbuf,fin,1,in_stride, st->factors,st); - //__android_log_print(ANDROID_LOG_INFO,"----","stride2"); - memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); + if(fout == NULL) { + __android_log_print(ANDROID_LOG_INFO,"KISS_FFT_STRIDE","fout buffer is NULL."); + return; + } - }else{ - //__android_log_print(ANDROID_LOG_INFO,"----","stride!!!!!"); - kf_work( fout, fin, 1,in_stride, st->factors,st ); + int tmpBufMemorySize = sizeof(kiss_fft_cpx) * st->nfft; + kiss_fft_cpx *tmpbuf = (kiss_fft_cpx*) KISS_FFT_MALLOC(tmpBufMemorySize); + if(tmpbuf == NULL) { + __android_log_print(ANDROID_LOG_INFO,"KISS_FFT_STRIDE","Memory allocation failed."); + return; + } + + kf_work(tmpbuf, fin,1, in_stride, st->factors,st); + memcpy(fout, tmpbuf, tmpBufMemorySize); + free(tmpbuf); + } else { + kf_work( fout, fin, 1, in_stride, st->factors,st ); } } @@ -391,20 +382,6 @@ void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) kiss_fft_stride(cfg,fin,fout,1); } - -/* not really necessary to call, but if someone is doing in-place ffts, they may want to free the - buffers from CHECKBUF - */ -void kiss_fft_cleanup(void) -{ - free(scratchbuf); - scratchbuf = NULL; - nscratchbuf=0; - free(tmpbuf); - tmpbuf=NULL; - ntmpbuf=0; -} - int kiss_fft_next_fast_size(int n) { while(1) { diff --git a/euphony/src/main/cpp/arms/kiss_fft.h b/euphony/src/main/cpp/arms/kiss_fft.h index 7ddc718..1cc06df 100644 --- a/euphony/src/main/cpp/arms/kiss_fft.h +++ b/euphony/src/main/cpp/arms/kiss_fft.h @@ -40,7 +40,7 @@ extern "C" { #include #include #include -#define kiss_fft_scalar short +#define kiss_fft_scalar short //int32_t #else # ifndef kiss_fft_scalar /* default is float */ @@ -101,13 +101,6 @@ void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout buffer and can be simply free()d when no longer needed*/ #define kiss_fft_free free -/* - Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up - your compiler output to call this before you exit. -*/ -void kiss_fft_cleanup(void); - - /* * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) */ diff --git a/euphony/src/main/cpp/arms/kiss_fftr.c b/euphony/src/main/cpp/arms/kiss_fftr.c index 0b1dd8b..79b8487 100644 --- a/euphony/src/main/cpp/arms/kiss_fftr.c +++ b/euphony/src/main/cpp/arms/kiss_fftr.c @@ -16,7 +16,7 @@ kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenme { int i; kiss_fftr_cfg st = NULL; - size_t subsize, memneeded; + size_t subsize = 0, memneeded; if (nfft & 1) { fprintf(stderr,"Real FFT optimization must be even.\n"); @@ -42,7 +42,8 @@ kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenme st->super_twiddles = st->tmpbuf + nfft; kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); - for (i = 0; i < nfft/2; ++i) { + int nfft_half = (nfft >> 1); + for (i = 0; i < nfft_half; ++i) { double phase = -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5); if (inverse_fft) @@ -52,7 +53,7 @@ kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenme return st; } -void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) +void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata, kiss_fft_cpx *freqdata) { /* input buffer timedata is stored row-wise */ @@ -63,12 +64,9 @@ void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *fr fprintf(stderr,"kiss fft usage error: improper alloc\n"); exit(1); } - // __android_log_print(ANDROID_LOG_INFO,"----","a"); ncfft = st->substate->nfft; - // __android_log_print(ANDROID_LOG_INFO,"----","b"); /*perform the parallel fft of two real signals packed in real,imag*/ kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); - // __android_log_print(ANDROID_LOG_INFO,"----","c"); /* The real part of the DC element of the frequency spectrum in st->tmpbuf * contains the sum of the even-numbered elements of the input time sequence * The imag part is the sum of the odd-numbered elements @@ -92,7 +90,8 @@ void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *fr freqdata[ncfft].i = freqdata[0].i = 0; #endif - for ( k=1;k <= ncfft/2 ; ++k ) { + int ncfft_half = (ncfft >> 1); + for ( k=1;k <= ncfft_half ; ++k ) { fpk = st->tmpbuf[k]; fpnk.r = st->tmpbuf[ncfft-k].r; fpnk.i = - st->tmpbuf[ncfft-k].i; From 20119a1cbbf6371b3c4d7eb9b9082ee11ac1fe8b Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 19:30:35 +0900 Subject: [PATCH 25/31] Added EuTxManagerTest's getting WaveSource Unit-Test > Unit-test is passed. --- .../java/co/euphony/tx/EuTxManagerTest.java | 10 ++++++++++ euphony/src/main/cpp/core/TxEngine.h | 2 ++ euphony/src/main/cpp/core/source/TxEngine.cpp | 13 ++++++++++++ euphony/src/main/cpp/native-connector.cpp | 20 +++++++++++++++++++ .../co/euphony/tx/EuTxNativeConnector.java | 7 +++++++ 5 files changed, 52 insertions(+) diff --git a/euphony/src/androidTest/java/co/euphony/tx/EuTxManagerTest.java b/euphony/src/androidTest/java/co/euphony/tx/EuTxManagerTest.java index 852820c..f1a299d 100644 --- a/euphony/src/androidTest/java/co/euphony/tx/EuTxManagerTest.java +++ b/euphony/src/androidTest/java/co/euphony/tx/EuTxManagerTest.java @@ -10,6 +10,8 @@ import java.util.Arrays; +import co.euphony.util.EuOption; + import static org.junit.Assert.*; @RunWith(Parameterized.class) @@ -65,6 +67,14 @@ public void setGetCodeTest() { } + @Test + public void getGenWaveSource() { + txManager.setCode(code); + txManager.setMode(EuOption.ModeType.DEFAULT); + float[] waveSourceData = txManager.getOutStream(); + assertEquals(waveSourceData.length, expectedStreamLength); + } + @Test public void testRun() { txManager.callEuPI(18000, EuTxManager.EuPIDuration.LENGTH_SHORT); diff --git a/euphony/src/main/cpp/core/TxEngine.h b/euphony/src/main/cpp/core/TxEngine.h index e432088..dcc4a72 100644 --- a/euphony/src/main/cpp/core/TxEngine.h +++ b/euphony/src/main/cpp/core/TxEngine.h @@ -26,6 +26,8 @@ namespace Euphony { void setCode(std::string data); std::string getCode(); std::string getGenCode(); + float* getGenWaveSource(); + int getGenWaveSourceSize(); void setCodingType(int codingTypeSrc); void setMode(int modeSrc); void setModulation(int modulationTypeSrc); diff --git a/euphony/src/main/cpp/core/source/TxEngine.cpp b/euphony/src/main/cpp/core/source/TxEngine.cpp index f6e1dfc..b099497 100644 --- a/euphony/src/main/cpp/core/source/TxEngine.cpp +++ b/euphony/src/main/cpp/core/source/TxEngine.cpp @@ -338,3 +338,16 @@ std::string TxEngine::getGenCode() { return pImpl->txPacket->toString(); } +float *Euphony::TxEngine::getGenWaveSource() { + if(pImpl->mAudioSource != nullptr) + return std::dynamic_pointer_cast(pImpl->mAudioSource)->getWaveSource(); + else + return nullptr; +} + +int Euphony::TxEngine::getGenWaveSourceSize() { + if(pImpl->mAudioSource != nullptr) + return std::dynamic_pointer_cast(pImpl->mAudioSource)->getWaveSourceSize(); + else + return 0; +} diff --git a/euphony/src/main/cpp/native-connector.cpp b/euphony/src/main/cpp/native-connector.cpp index cf3f921..2cdcedc 100644 --- a/euphony/src/main/cpp/native-connector.cpp +++ b/euphony/src/main/cpp/native-connector.cpp @@ -120,6 +120,26 @@ extern "C" { return env->NewStringUTF(engine->getGenCode().c_str()); } + JNIEXPORT jfloatArray JNICALL + Java_co_euphony_tx_EuTxNativeConnector_native_1getGenWaveSource(JNIEnv *env, jobject thiz, + jlong engine_handle) { + jfloatArray result; + + auto engine = reinterpret_cast (engine_handle); + if(engine == nullptr) { + LOGE("Engine handle is invalid, call createHandle() to create a new one"); + return nullptr; + } + + float* genWaveSource = engine->getGenWaveSource(); + int genWaveSourceSize = engine->getGenWaveSourceSize(); + + result = env->NewFloatArray(genWaveSourceSize); + env->SetFloatArrayRegion(result, 0, genWaveSourceSize, genWaveSource); + + return result; + } + JNIEXPORT void JNICALL Java_co_euphony_tx_EuTxNativeConnector_native_1start(JNIEnv *env, jobject thiz, jlong engine_handle) { diff --git a/euphony/src/main/java/co/euphony/tx/EuTxNativeConnector.java b/euphony/src/main/java/co/euphony/tx/EuTxNativeConnector.java index d3cae2c..5e3149d 100644 --- a/euphony/src/main/java/co/euphony/tx/EuTxNativeConnector.java +++ b/euphony/src/main/java/co/euphony/tx/EuTxNativeConnector.java @@ -156,6 +156,12 @@ public String getGenCode() { return null; } + public float[] getGenWaveSource() { + if(mEngineHandle != 0) + return native_getGenWaveSource(mEngineHandle); + return null; + } + public void setAudioFrequency(double freq) { if(mEngineHandle != 0) native_setAudioFrequency(mEngineHandle, freq); } @@ -215,6 +221,7 @@ public EpnyStatus getStatus() { private native void native_setModulation(long engineHandle, int modulationType); private native String native_getCode(long engineHandle); private native String native_getGenCode(long engineHandle); + private native float[] native_getGenWaveSource(long engineHandle); private native void native_setAudioFrequency(long engineHandle, double frequency); private native void native_setAudioApi(long engineHandle, int audioApi); private native void native_setAudioDeviceId(long engineHandle, int deviceId); From 90785b89e16b87032b35e4b629ad684e65779c77 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 19:32:20 +0900 Subject: [PATCH 26/31] Rearranged Global Constants & Applied EuRxManager & EuFreqObject for Rx --- .../java/co/euphony/common/Constants.java | 13 +- .../main/java/co/euphony/rx/EuFreqObject.java | 187 ++++----- .../main/java/co/euphony/rx/EuRxManager.java | 394 +++++++++++++++--- 3 files changed, 427 insertions(+), 167 deletions(-) diff --git a/euphony/src/main/java/co/euphony/common/Constants.java b/euphony/src/main/java/co/euphony/common/Constants.java index b7f546e..bd5bef7 100644 --- a/euphony/src/main/java/co/euphony/common/Constants.java +++ b/euphony/src/main/java/co/euphony/common/Constants.java @@ -4,15 +4,16 @@ public class Constants { // RX & TX COMMON VARIABLES public static final int SAMPLERATE = 44100; + public static final int HALF_SAMPLERATE = (SAMPLERATE >> 1); public static final int FFT_SIZE = 512; - public static final int DATA_LENGTH = FFT_SIZE * 4; - public static final int FADE_RANGE = DATA_LENGTH / 16; - public static final double MAX_FREQ = 22050.0; - public static final int START_FREQ = 18000; public static final int CHANNEL = 16; - public static final int CHANNEL_SPAN = SAMPLERATE / FFT_SIZE; // Frequency Interval - public static final int BUNDLE_INTERVAL = CHANNEL_SPAN * CHANNEL; + public static final int CHANNEL_INTERVAL = SAMPLERATE / FFT_SIZE; // Frequency Interval + + public static final int STANDARD_FREQ = 18001; + public static final int START_SIGNAL_FREQ = STANDARD_FREQ - CHANNEL_INTERVAL; + + public static final int BUNDLE_INTERVAL = CHANNEL_INTERVAL * CHANNEL; // RX public static final int MAX_REF = 4000; diff --git a/euphony/src/main/java/co/euphony/rx/EuFreqObject.java b/euphony/src/main/java/co/euphony/rx/EuFreqObject.java index 8376d52..2f7414e 100644 --- a/euphony/src/main/java/co/euphony/rx/EuFreqObject.java +++ b/euphony/src/main/java/co/euphony/rx/EuFreqObject.java @@ -12,20 +12,11 @@ public class EuFreqObject { - public final int SAMPLERATE = Constants.SAMPLERATE;//44100; - public final int fftsize = Constants.FFT_SIZE;//512; - public final int MAXREFERENCE = Constants.MAX_REF;//4000; - public final int MINREFERENCE = Constants.MIN_REF;//50; - public final double MAXFREQUENCY = Constants.MAX_FREQ;//22050.0; - public final int DEFAULT_REF = Constants.DEFAULT_REF;//500 - public final int START_FREQ = Constants.START_FREQ; //18000 - public final int RXCHANNEL = Constants.CHANNEL;// 16 - private final int STARTCHANNEL = 1; - private int mFreqSpan = Constants.CHANNEL_SPAN; // 86 - public final int START_BIT = START_FREQ - mFreqSpan; - public final int START_BIT_IDX = RXCHANNEL; - public int[] DATA_FREQ = new int[RXCHANNEL]; - public int[] DATA_FREQ_INDEX_FOR_FFT = new int[RXCHANNEL + 1]; + private final int STARTCHANNEL = 1; + public final int START_BIT_IDX = Constants.CHANNEL; + + public int[] DATA_FREQ = new int[Constants.CHANNEL]; + public int[] DATA_FREQ_INDEX_FOR_FFT = new int[Constants.CHANNEL + 1]; private Boolean isStarted = false; private Boolean isCompleted = false; @@ -33,60 +24,51 @@ public class EuFreqObject { public Boolean getStarted() { return isStarted; } - public void setStarted(Boolean started) { isStarted = started; } - public Boolean getCompleted() { return isCompleted; } - public void setCompleted(Boolean completed) { isCompleted = completed; } - private Boolean isRecording = false; - private ByteBuffer samples = allocateByteBuffer(fftsize); - private FloatBuffer spectrum = allocateFloatBuffer(fftsize/2+1); - private FloatBuffer spectrum_p = allocateFloatBuffer(fftsize/2+1); - public static String receiveStr = ""; + private ByteBuffer samples = allocateByteBuffer(Constants.FFT_SIZE); + private FloatBuffer spectrum = allocateFloatBuffer(Constants.FFT_SIZE / 2 + 1); + private FloatBuffer spectrum_p = allocateFloatBuffer(Constants.FFT_SIZE / 2 + 1); private String mReceivedData; - public String getReceivedData() { - return mReceivedData; - } + private int[] mFreqArray = new int[Constants.CHANNEL + STARTCHANNEL]; + private int[] mTempRef = new int[Constants.CHANNEL + STARTCHANNEL]; + private int[] mRefCntIndexArray = new int[Constants.CHANNEL + STARTCHANNEL]; + private int[] mDynamicRefArray = new int[Constants.CHANNEL + STARTCHANNEL]; - - public void setReceivedData(String _receivedData) { - this.mReceivedData = _receivedData; - } - - private int[] mFreqArray = new int[RXCHANNEL + STARTCHANNEL]; - private int[] mTempRef = new int[RXCHANNEL + STARTCHANNEL]; - private int[] mRefCntIndexArray = new int[RXCHANNEL + STARTCHANNEL]; - private int[] mDynamicRefArray = new int[RXCHANNEL + STARTCHANNEL]; - - private ArrayList mChannelArrayList = new ArrayList(); + private ArrayList mChannelArrayList = new ArrayList<>(); private AudioRecorder recorder; - private KissFFT FFT = new KissFFT(fftsize); + private KissFFT FFT = new KissFFT(Constants.FFT_SIZE); + + private int []mArrMaxIndex = new int [Constants.CHANNEL + STARTCHANNEL]; + private int []mArrSampleIndex = new int [Constants.CHANNEL + STARTCHANNEL]; + private int []mArrSampleTemp = new int [Constants.CHANNEL + STARTCHANNEL]; + private int []mArrChannelTemp = new int [Constants.CHANNEL + STARTCHANNEL]; + public int []mArrcntCheck = new int [Constants.CHANNEL + STARTCHANNEL]; public EuFreqObject() { - recorder = new AudioRecorder(SAMPLERATE); + recorder = new AudioRecorder(Constants.SAMPLERATE); //INIT DYNAMIC REFERENCE ARRAY.. - for (int i = 0; i < RXCHANNEL; i++) { - DATA_FREQ[i] = START_FREQ + mFreqSpan * i; - DATA_FREQ_INDEX_FOR_FFT[i] = ((int)((DATA_FREQ[i] / MAXFREQUENCY) * fftsize / 2)) + 1; - mDynamicRefArray[i] = DEFAULT_REF; + for (int i = 0; i < Constants.CHANNEL; i++) { + DATA_FREQ[i] = Constants.STANDARD_FREQ + Constants.CHANNEL_INTERVAL * i; + DATA_FREQ_INDEX_FOR_FFT[i] = Math.round((DATA_FREQ[i] / (float)Constants.HALF_SAMPLERATE) * (Constants.FFT_SIZE >> 1)); + mDynamicRefArray[i] = Constants.DEFAULT_REF; } - DATA_FREQ_INDEX_FOR_FFT[RXCHANNEL] = ((int)((START_BIT / MAXFREQUENCY) * fftsize / 2)) + 1; - mDynamicRefArray[RXCHANNEL] = DEFAULT_REF; + DATA_FREQ_INDEX_FOR_FFT[Constants.CHANNEL] = Math.round((Constants.START_SIGNAL_FREQ / (float)Constants.HALF_SAMPLERATE) * (Constants.FFT_SIZE >> 1)); + mDynamicRefArray[Constants.CHANNEL] = Constants.DEFAULT_REF; - isRecording = false; } @@ -97,7 +79,7 @@ public void processFFT() recorder.start(); isRecording = true; } - recorder.read(samples, fftsize); + recorder.read(samples, Constants.FFT_SIZE); FFT.doSpectrums(samples, spectrum); } @@ -107,7 +89,7 @@ public void processFFT(short windowsNum) recorder.start(); isRecording = true; } - recorder.read(samples, fftsize, windowsNum); + recorder.read(samples, Constants.FFT_SIZE, windowsNum); FFT.doSpectrums(samples, spectrum); } @@ -119,33 +101,25 @@ public void destroyFFT() } public int detectFreq(int fFrequency){ - - double fFreqRatio; - int freqIndex; - float fmax; - - fFreqRatio = fFrequency / MAXFREQUENCY; - freqIndex = ( (int) (fFreqRatio * fftsize / 2) ) + 1; - - //f1 = spectrum.get(freqIndex-1); - fmax = spectrum.get(freqIndex); - //f3 = spectrum_p.get(freqIndex); - //r1 = real.get(freqIndex); - //i1 = image.get(freqIndex); - /* - String s = ""; - for(int i = freqIndex - 1; i <= freqIndex + 1; i++) - s += " " + spectrum.get(i); - Log.i("HELLO FREQ", freqIndex + "::" + s); - */ - //f3 = spectrum.get(freqIndex+1); - - //if( fmax < f2 ) fmax = f2; - //if( fmax < f3 ) fmax = f3; + final float fFreqRatio = fFrequency / (float) Constants.HALF_SAMPLERATE; + final int freqIndex = Math.round(fFreqRatio * (Constants.FFT_SIZE >> 1)); + final float fmax = spectrum.get(freqIndex); return (int)(fmax*100000); } + public String getReceivedData() { + return mReceivedData; + } + + public void setReceivedData(String _receivedData) { + this.mReceivedData = _receivedData; + } + + public float getSpectrumValue(int idx) { + return spectrum.get(idx); + } + public int detectFreqByIdx(int idx) { float fmax = spectrum.get(DATA_FREQ_INDEX_FOR_FFT[idx]); return (int) (fmax * 100000); @@ -167,13 +141,13 @@ public void catchSingleData() int currentFreq = 0; // UPDATED ON 1/2/2014 // START BIT's Frequency Detection - mSampleTemp = detectFreqByIdx(START_BIT_IDX); - mMaxIndex = RXCHANNEL; + mSampleTemp = detectFreqByIdx(Constants.CHANNEL); + mMaxIndex = Constants.CHANNEL; // START BIT's Dynamic Reference Catch - mDynamicRefArray[RXCHANNEL] = getDynamicReference(mSampleTemp, RXCHANNEL); + mDynamicRefArray[Constants.CHANNEL] = getDynamicReference(mSampleTemp, Constants.CHANNEL); // Rest of frequency processing - for(int i = 0; i < RXCHANNEL; i++) + for(int i = 0; i < Constants.CHANNEL; i++) { currentFreq = detectFreqByIdx(i); mDynamicRefArray[i] = getDynamicReference(currentFreq, i); @@ -193,14 +167,13 @@ public void catchSingleData() } else { - for(int i = 0; i < RXCHANNEL + STARTCHANNEL ; i++) + for(int i = 0; i < Constants.CHANNEL + STARTCHANNEL; i++) { - if(mFreqArray[i]>mChannelTemp) + if(mFreqArray[i] > mChannelTemp) { mChannelTemp = mFreqArray[i]; mMaxIndex = i; } - //mbFreqArray[i] = 0; } if(mChannelTemp > 2 && mMaxIndex != -1) @@ -219,11 +192,14 @@ public void catchSingleData() else mReceivedData += "" + a[i]; } - - if(PacketErrorDetector.makeCheckSum(a) == mChannelArrayList.get(mChannelArrayList.size()-2) && (PacketErrorDetector.makeParellelParity(a) == mChannelArrayList.get(mChannelArrayList.size()-1))) { + + if(PacketErrorDetector.makeCheckSum(a) == mChannelArrayList.get(mChannelArrayList.size()-2) && (PacketErrorDetector.makeParallelParity(a) == mChannelArrayList.get(mChannelArrayList.size()-1))) { isStarted = false; isCompleted = true; - receiveStr = EuDataDecoder.decodeStaticHexCharSource(a); + } else { + Log.v("DATA","Parity Error Check = " + PacketErrorDetector.makeParallelParity(a)); + Log.v("DATA","Checksum Error Check = " + PacketErrorDetector.makeCheckSum(a)); + Log.v("DATA","ReceivedData = " + mReceivedData); } mChannelArrayList.clear(); } @@ -247,20 +223,16 @@ public void catchSingleData() mSampleIndex = 0 ; mSampleTemp = 0; mChannelTemp = 0; - for(int i = 0; i < RXCHANNEL + STARTCHANNEL ; i++){//// - mFreqArray[i] = 0;//// - }///// + for(int i = 0; i < Constants.CHANNEL + STARTCHANNEL ; i++){ + mFreqArray[i] = 0; + } } } - private int []mArrMaxIndex = new int [RXCHANNEL + STARTCHANNEL]; - private int []mArrSampleIndex = new int [RXCHANNEL + STARTCHANNEL]; - private int []mArrSampleTemp = new int [RXCHANNEL + STARTCHANNEL]; - private int []mArrChannelTemp = new int [RXCHANNEL + STARTCHANNEL]; - public int []mArrcntCheck = new int [RXCHANNEL + STARTCHANNEL]; + public void catchMultiData() { - for(int j = 0; j < RXCHANNEL + STARTCHANNEL ; j++) + for(int j = 0; j < Constants.CHANNEL + STARTCHANNEL ; j++) { mArrMaxIndex[j] = -1; mArrSampleIndex[j] = 0; @@ -269,10 +241,11 @@ public void catchMultiData() mArrcntCheck[j] = 0; mFreqArray[j] = 0; } - int []arrCurrentFreq = new int[RXCHANNEL + STARTCHANNEL]; - for(int i = 0; i < RXCHANNEL + STARTCHANNEL ; i++) + + int []arrCurrentFreq = new int[Constants.CHANNEL + STARTCHANNEL]; + for(int i = 0; i < Constants.CHANNEL + STARTCHANNEL ; i++) { - arrCurrentFreq[i] = (int) this.detectFreq(START_FREQ + mFreqSpan*i); + arrCurrentFreq[i] = (int) this.detectFreq(Constants.STANDARD_FREQ + Constants.CHANNEL_INTERVAL * i); if(arrCurrentFreq[i] >=200){ //mArrSampleTemp[i] = arrCurrentFreq[i]; mArrMaxIndex[i] = i; @@ -294,16 +267,6 @@ public void setmArrMaxIndex(int[] mArrMaxIndex) { this.mArrMaxIndex = mArrMaxIndex; } - /* - public void makeData(int a[]){ - for(int i = 0; i < a.length-1; i+=2){ - char charData = (char)(a[i]*16 + a[i+1]); - receiveStr += charData; - Log.d("charData", a[i]+" "+a[i+1]+" "+receiveStr); - } - } - */ - public int mStartSampleCnt = 0; public Boolean checkStartPoint() { @@ -348,8 +311,8 @@ private Boolean euSpecificFreqSensor(int _freq) dynRef = euGetDynamicRef(curFreq, dynRef, preservedRef, dynRefCnt); if(curFreq >= dynRef){ - int prevFreq = this.detectFreq(_freq - mFreqSpan); - int nextFreq = this.detectFreq(_freq + mFreqSpan); + int prevFreq = this.detectFreq(_freq - Constants.CHANNEL_INTERVAL); + int nextFreq = this.detectFreq(_freq + Constants.CHANNEL_INTERVAL); if(prevFreq > curFreq || nextFreq > curFreq) continue; @@ -399,10 +362,10 @@ public int euGetDynamicRef(int curFreq, int dynRef, int preservedRef, int dynRef } // MAXIMUM and MIMINUM DATA CATCHING - if(dynRef > MAXREFERENCE) - dynRef = MAXREFERENCE; - if(dynRef < MINREFERENCE) - dynRef = MINREFERENCE; + if(dynRef > Constants.MAX_REF) + dynRef = Constants.MAX_REF; + if(dynRef < Constants.MIN_REF) + dynRef = Constants.MIN_REF; return dynRef; } @@ -434,10 +397,10 @@ public int getDynamicReference(int nfreq, int freqIndex) } // MAXIMUM and MIMINUM DATA CATCHING - if(mDynamicRefArray[freqIndex] > MAXREFERENCE) - mDynamicRefArray[freqIndex] = MAXREFERENCE; - if(mDynamicRefArray[freqIndex] < MINREFERENCE) - mDynamicRefArray[freqIndex] = MINREFERENCE; + if(mDynamicRefArray[freqIndex] > Constants.MAX_REF) + mDynamicRefArray[freqIndex] = Constants.MAX_REF; + if(mDynamicRefArray[freqIndex] < Constants.MIN_REF) + mDynamicRefArray[freqIndex] = Constants.MIN_REF; return mDynamicRefArray[freqIndex]; } diff --git a/euphony/src/main/java/co/euphony/rx/EuRxManager.java b/euphony/src/main/java/co/euphony/rx/EuRxManager.java index 6df599d..ef65a8f 100644 --- a/euphony/src/main/java/co/euphony/rx/EuRxManager.java +++ b/euphony/src/main/java/co/euphony/rx/EuRxManager.java @@ -1,51 +1,179 @@ package co.euphony.rx; -import co.euphony.common.Constants; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.util.Log; +import java.util.ArrayList; + +import co.euphony.common.Constants; +import co.euphony.util.EuOption; + +import static co.euphony.rx.EuPI.EuPITrigger.KEY_DOWN; +import static co.euphony.rx.EuPI.EuPITrigger.KEY_PRESSED; +import static co.euphony.rx.EuPI.EuPITrigger.KEY_UP; + public class EuRxManager { - private Thread mRxThread = null; - private RxRunner mRxRunner = null; - private boolean _active; - - private static final int RX_DECODE = 1; - private boolean mHex = false; - public EuRxManager() { } - public EuRxManager(boolean hex) { mHex = hex; } - - public void listen() - { - listen(mHex); + private final String LOG = "EuRxManager"; + + private Thread mListenThread = null; + private DetectRunner mDetectRunner = null; + private EuPICallRunner mEuPICallRunner = null; + + public enum RxManagerStatus { + RUNNING, STOP } - public void listen(boolean hex) { - _active = true; - mRxRunner = new RxRunner(hex); - mRxThread = new Thread(mRxRunner, "RX"); - mRxThread.start(); + private static final int RX_MODE = 1; + private static final int DETECT_MODE = 2; + private static final int EUPI_CALL_MODE = 3; + + private EuOption mOption; + + public EuRxManager() { + mOption = EuOption.builder() + .modeWith(EuOption.ModeType.DEFAULT) + .encodingWith(EuOption.CodingType.BASE16) + .modulationWith(EuOption.ModulationType.FSK) + .build(); + } + + public EuRxManager(EuOption.ModeType mode) { + mOption = EuOption.builder() + .modeWith(mode) + .build(); } - public void finish() - { - if(mRxThread != null) { - _active = false; - while (true) { - try { - mRxThread.join(); + public boolean listen() { + if(getStatus() != RxManagerStatus.RUNNING) { + switch (mOption.getMode()) { + case DEFAULT: + mListenThread = new Thread(new RxRunner(), "RX"); break; - } catch (InterruptedException e) { - Log.i("FINISH", e.getMessage()); + case EUPI: { + if(mEuPICallRunner != null) { + mListenThread = new Thread(mEuPICallRunner, "EUPI"); + Log.d(LOG, "Euphony : EuPICallRunner's EuPI Count : " + mEuPICallRunner.getEuPICount()); + break; + } + else { + Log.d(LOG, "Euphony : EuPICallRunner is null"); + return false; + } } + default: + Log.d(LOG, "Detect must have specific frequency value"); + return false; + } + mListenThread.start(); + return true; + } else { + return false; + } + } + + public boolean listen(int freq) { + if(getStatus() != RxManagerStatus.RUNNING) { + if(mOption.getMode() == EuOption.ModeType.DETECT) { + mDetectRunner = new DetectRunner(freq); + mListenThread = new Thread(mDetectRunner, "DETECT"); + mListenThread.start(); + return true; + } else { + Log.d(LOG, "Please use other listen function."); + return false; } + } else { + return false; + } + } + + public void finish() + { + if(mListenThread != null) { + mListenThread.interrupt(); } - if(mRxRunner != null) - mRxRunner.destroyFFT(); - mRxThread = null; - mRxRunner = null; + mListenThread = null; + } + + public void setOnWaveKeyPressed(int freq, EuPICallDetector iEuPICallDetector) { + EuPI eupi = new EuPI(freq, KEY_PRESSED, iEuPICallDetector); + + if(mEuPICallRunner == null) { + mEuPICallRunner = new EuPICallRunner(eupi); + } else { + mEuPICallRunner.addEuPI(eupi); + } + } + + public void setOnWaveKeyDown(int key, EuPICallDetector iEuPICallDetector) { + EuPI eupi = new EuPI(key, KEY_DOWN, iEuPICallDetector); + + if(mEuPICallRunner == null) { + mEuPICallRunner = new EuPICallRunner(eupi); + } else { + mEuPICallRunner.addEuPI(eupi); + } + } + + public void setOnWaveKeyUp(int key, EuPICallDetector iEuPICallDetector) { + EuPI eupi = new EuPI(key, KEY_UP, iEuPICallDetector); + + if(mEuPICallRunner == null) { + mEuPICallRunner = new EuPICallRunner(eupi); + } else { + mEuPICallRunner.addEuPI(eupi); + } + } + + public void setOnWaveKeyPressed(int freq, double threshold, EuPICallDetector iEuPICallDetector) { + EuPI eupi = new EuPI(freq, KEY_PRESSED, iEuPICallDetector); + + if(mEuPICallRunner == null) { + mEuPICallRunner = new EuPICallRunner(threshold, eupi); + } else { + mEuPICallRunner.addEuPI(eupi); + } + } + + public void setOnWaveKeyDown(int key, double threshold, EuPICallDetector iEuPICallDetector) { + EuPI eupi = new EuPI(key, KEY_DOWN, iEuPICallDetector); + + if(mEuPICallRunner == null) { + mEuPICallRunner = new EuPICallRunner(threshold, eupi); + } else { + mEuPICallRunner.addEuPI(eupi); + } + } + + public void setOnWaveKeyUp(int key, double threshold, EuPICallDetector iEuPICallDetector) { + EuPI eupi = new EuPI(key, KEY_UP, iEuPICallDetector); + + if(mEuPICallRunner == null) { + mEuPICallRunner = new EuPICallRunner(threshold, eupi); + } else { + mEuPICallRunner.addEuPI(eupi); + } + } + + public RxManagerStatus getStatus() { + if(mListenThread != null) { + switch (mListenThread.getState()) { + case RUNNABLE: + return RxManagerStatus.RUNNING; + case NEW: + case WAITING: + case TIMED_WAITING: + case BLOCKED: + case TERMINATED: + default: + return RxManagerStatus.STOP; + } + } else + return RxManagerStatus.STOP; } private AcousticSensor mAcousticSensor; @@ -58,45 +186,213 @@ public void setAcousticSensor(AcousticSensor iAcousticSensor) { this.mAcousticSensor = iAcousticSensor; } - private Handler mHandler = new Handler(){ + private final Handler mHandler = new Handler(Looper.getMainLooper()){ public void handleMessage(Message msg){ switch(msg.what){ - case RX_DECODE: - mAcousticSensor.notify(msg.obj + ""); - break; + case RX_MODE: + mAcousticSensor.notify(msg.obj + ""); + break; + case DETECT_MODE: + mFrequencyDetector.detect((float)msg.obj); + break; + case EUPI_CALL_MODE: + EuPI eupi = (EuPI)msg.obj; + eupi.getCallback().call(); + break; + default: break; } } }; + public EuOption getOption() { + return mOption; + } + + public void setOption(EuOption mOption) { + this.mOption = mOption; + } + private class RxRunner extends EuFreqObject implements Runnable{ - boolean mHex = false; - RxRunner() { } - RxRunner(boolean hex) { - mHex = hex; - } @Override public void run() { - while (_active) - { + while (!Thread.currentThread().isInterrupted()) { processFFT(); - if(this.getStarted()) + if (this.getStarted()) catchSingleData(); else this.setStarted(checkStartPoint()); - - if(this.getCompleted()){ + + if (this.getCompleted()) { Message msg = mHandler.obtainMessage(); - msg.what = RX_DECODE; - msg.obj = (mHex) ? getReceivedData() : EuDataDecoder.decodeStaticHexCharSource(getReceivedData()); + msg.what = RX_MODE; + msg.obj = null; + if(mOption.getCodingType() == EuOption.CodingType.BASE16) { + msg.obj = EuDataDecoder.decodeStaticHexCharSource(getReceivedData()); + } this.setCompleted(false); mHandler.sendMessage(msg); - mRxRunner.destroyFFT(); + destroyFFT(); return; } - } + } + + destroyFFT(); + } + } + + private FrequencyDetector mFrequencyDetector; + + public FrequencyDetector getFrequencyDetector() { + return mFrequencyDetector; + } + + public void setFrequencyDetector(FrequencyDetector mFrequencyDetector) { + this.mFrequencyDetector = mFrequencyDetector; + } + + public void setFrequencyForDetect(int freq) { + if(mOption.getMode() == EuOption.ModeType.DETECT) { + if(mDetectRunner != null) + mDetectRunner.setFrequency(freq); + } + } + + private class EuPICallRunner extends EuFreqObject implements Runnable { + + private double mThreshold = 0.0009; + private final ArrayList EuPICallList = new ArrayList<>(); + + EuPICallRunner(EuPI eupi) { + addEuPI(eupi); + Log.d(LOG, "Added " + eupi.getKey() + "(" + eupi.getFreqIndex() + ")"); + } + + EuPICallRunner(double threshold, EuPI eupi) { + mThreshold = threshold; + addEuPI(eupi); + Log.d(LOG, "Added " + eupi.getKey() + "(" + eupi.getFreqIndex() + ")"); + } + + private int calculateFreqIndex(int freq) { + double freqRatio = ((float)freq) / (float)Constants.HALF_SAMPLERATE; + return (int) Math.round((freqRatio * (float) (Constants.FFT_SIZE >> 1))); + } + + private boolean compareThreshold(float amp) { + return amp > mThreshold; + } + + public void addEuPI(EuPI eupi) { + eupi.setFreqIndex(calculateFreqIndex(eupi.getKey())); + EuPICallList.add(eupi); + } + + public int getEuPICount() { + return EuPICallList.size(); + } + + @Override + public void run() { + while(!Thread.currentThread().isInterrupted()) { + processFFT(); + + float[] amp = {0, 0, 0}; + for(EuPI eupi : EuPICallList) { + boolean isActable = false; + switch(eupi.getTrigger()) { + case KEY_DOWN: { + if(eupi.getStatus() == EuPI.EuPIStatus.KEY_UP){ + isActable = true; + } + } + break; + case KEY_UP: { + if(eupi.getStatus() != EuPI.EuPIStatus.KEY_UP) { + isActable = true; + } + } + break; + case KEY_PRESSED: { + isActable = true; + } + break; + } + + int freqIndex = eupi.getFreqIndex(); + amp[0] = getSpectrumValue(freqIndex - 2); + amp[1] = getSpectrumValue(freqIndex); + amp[2] = getSpectrumValue(freqIndex + 2); + + if(isActable) { + if(compareThreshold((amp[1] - (amp[0] + amp[2])/2))) { + if(eupi.getTrigger() == KEY_DOWN || eupi.getTrigger() == KEY_PRESSED) { + Message msg = mHandler.obtainMessage(); + msg.what = EUPI_CALL_MODE; + msg.obj = eupi; + mHandler.sendMessage(msg); + } + + eupi.setStatus(EuPI.EuPIStatus.KEY_DOWN); + } else { + if(eupi.getTrigger() == KEY_UP) { + Message msg = mHandler.obtainMessage(); + msg.what = EUPI_CALL_MODE; + msg.obj = eupi; + mHandler.sendMessage(msg); + } + + eupi.setStatus(EuPI.EuPIStatus.KEY_UP); + } + } else { + if(compareThreshold((amp[1] - (amp[0] + amp[2])/2))) { + eupi.setStatus(EuPI.EuPIStatus.KEY_DOWN); + } else { + eupi.setStatus(EuPI.EuPIStatus.KEY_UP); + } + } + + Log.d(LOG, eupi.getKey() + "(" + eupi.getFreqIndex() + ")" + "'s Amplitude : " + amp[2]); + } + } + + destroyFFT(); + } + } + + private class DetectRunner extends EuFreqObject implements Runnable { + + int mFrequency = 0; + private int mFreqIndex = 0; + DetectRunner(int freq) { + setFrequency(freq); + } + + public void setFrequency(int frequency) { + mFrequency = frequency; + mFreqIndex = ((int)((frequency / 22050.0) * Constants.FFT_SIZE) / 2) + 1; + Log.d(LOG, "Frequency = " + mFrequency + ", mFreqIndex = " + mFreqIndex); + } + + @Override + public void run() { + float previousAmp = 0; + + while (!Thread.currentThread().isInterrupted()) { + processFFT(); + float amp = getSpectrumValue(mFreqIndex); + + if (previousAmp != amp) { + Message msg = mHandler.obtainMessage(); + msg.what = DETECT_MODE; + msg.obj = amp; + mHandler.sendMessage(msg); + previousAmp = amp; + } + } + destroyFFT(); } } } From b2314264b4da4910d117177b0d0e7b3516fef924 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 19:33:59 +0900 Subject: [PATCH 27/31] Fixed typo (parellel -> parallel) --- .../src/main/java/co/euphony/util/PacketErrorDetector.java | 4 ++-- euphony/src/test/java/euphony/lib/util/UtilUnitTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/euphony/src/main/java/co/euphony/util/PacketErrorDetector.java b/euphony/src/main/java/co/euphony/util/PacketErrorDetector.java index 41bd564..3d9d484 100644 --- a/euphony/src/main/java/co/euphony/util/PacketErrorDetector.java +++ b/euphony/src/main/java/co/euphony/util/PacketErrorDetector.java @@ -64,7 +64,7 @@ public static boolean checkEvenParity(int[] payload, int nParityBit){ * return type : int * the EvenParity data to transmit *****************************************************/ - public static int makeParellelParity(int payLoad){ + public static int makeParallelParity(int payLoad){ int evenParity1 = ((0x8 & payLoad) >> 3); int evenParity2 = ((0x4 & payLoad) >> 2); int evenParity3 = ((0x2 & payLoad) >> 1); @@ -85,7 +85,7 @@ public static int makeParellelParity(int payLoad){ * return type : int * the EvenParity data to transmit *****************************************************/ - public static int makeParellelParity(int[] payLoad){ + public static int makeParallelParity(int[] payLoad){ int evenParity1 = 0; int evenParity2 = 0; int evenParity3 = 0; diff --git a/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java b/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java index 8c54b93..16dc80c 100644 --- a/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java +++ b/euphony/src/test/java/euphony/lib/util/UtilUnitTest.java @@ -17,8 +17,8 @@ public void packet_err_detect_isCorrect() int[] source = {0x6, 0x8, 0x6, 0x5, 0x6, 0xc, 0x6, 0xc, 0x6, 0xf}; assertEquals(PacketErrorDetector.makeCheckSum(source), 14); assertEquals(PacketErrorDetector.makeCheckSum( 234), 6); - assertEquals(PacketErrorDetector.makeParellelParity(source), 4); - assertEquals(PacketErrorDetector.makeParellelParity(234), 10); + assertEquals(PacketErrorDetector.makeParallelParity(source), 4); + assertEquals(PacketErrorDetector.makeParallelParity(234), 10); assertTrue(PacketErrorDetector.checkEvenParity(source, 4)); assertFalse(PacketErrorDetector.checkEvenParity(source, 5)); assertTrue(PacketErrorDetector.verifyCheckSum(source, 14)); From 518c38eb6f1cbb990807b2a49466bdc596b5c266 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 19:37:13 +0900 Subject: [PATCH 28/31] Made PlayerEngine Concept > ANDROID_DEFAULT_ENGINE : it is using AudioTrack class (Android API). that was using on v0.7.1.6 > EUPHONY_NATIVE_ENGINE : it is applied on v0.8. there is some amplitude issue. need to be constantly updated. Default value is ANDROID_DEFAULT_ENGINE. because EUPHONY_NATIVE_ENGINE has some amplitude issue. it is experimental. --- .../main/java/co/euphony/tx/EuTxManager.java | 98 +++++++++++++++---- 1 file changed, 81 insertions(+), 17 deletions(-) diff --git a/euphony/src/main/java/co/euphony/tx/EuTxManager.java b/euphony/src/main/java/co/euphony/tx/EuTxManager.java index fd8e89d..25f88ec 100644 --- a/euphony/src/main/java/co/euphony/tx/EuTxManager.java +++ b/euphony/src/main/java/co/euphony/tx/EuTxManager.java @@ -1,13 +1,23 @@ package co.euphony.tx; import android.content.Context; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioTrack; import android.os.Handler; import android.os.Looper; +import android.util.Log; import co.euphony.util.EuOption; +import static android.media.AudioTrack.SUCCESS; +import static android.media.AudioTrack.WRITE_BLOCKING; + public class EuTxManager { private EuTxNativeConnector txCore; + private AudioTrack mAudioTrack = null; + private EuOption.ModeType modeType; + private PlayerEngine playerEngineType; public enum EuPIDuration { LENGTH_SHORT, @@ -15,6 +25,11 @@ public enum EuPIDuration { LENGTH_FOREVER } + public enum PlayerEngine { + ANDROID_DEFAULT_ENGINE, + EUPHONY_NATIVE_ENGINE + } + public EuTxManager(Context context) { txCore = new EuTxNativeConnector(context); } @@ -37,7 +52,7 @@ public String getCode() { } public void callEuPI(double freq, EuPIDuration duration) { - txCore.setMode(EuOption.ModeType.EUPI); + setMode(EuOption.ModeType.EUPI); txCore.setToneOn(true); txCore.setAudioFrequency(freq); txCore.start(); @@ -48,46 +63,95 @@ public void callEuPI(double freq, EuPIDuration duration) { } } + public float[] getOutStream() { + return txCore.getGenWaveSource(); + } + public String getGenCode() { return txCore.getGenCode(); } - public short[] getOutStream() { + public void play() { + play(1, PlayerEngine.ANDROID_DEFAULT_ENGINE); + } - /* TODO: legacy code will be removed. - That will be bring from EuphonyTx. - return mOutStream; - */ - return null; + public void play(final int count) { + play(count, PlayerEngine.ANDROID_DEFAULT_ENGINE); } - public void play() { - play(1); + public void play(final int count, PlayerEngine engineType) { + playerEngineType = engineType; + setMode(EuOption.ModeType.DEFAULT); + if(engineType == PlayerEngine.EUPHONY_NATIVE_ENGINE) { + playWithNativeEngine(count); + } else { + playWithAndroidEngine(count); + } } - public void play(int count) { - txCore.setMode(EuOption.ModeType.DEFAULT); + private void playWithNativeEngine(final int count) { txCore.setCountToneOn(true, count); txCore.start(); } + short[] outShortStream; + private void playWithAndroidEngine(int count) { + float[] outStream = txCore.getGenWaveSource(); + + mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, outStream.length*2, AudioTrack.MODE_STATIC); + if(count <= 0) + count = -1; + + int result = mAudioTrack.setLoopPoints(0, outStream.length, count); + if(result != SUCCESS) { + Log.i("PROCESS", "failed to loop points : " + result); + } + + outShortStream = new short[outStream.length]; + for(int i = 0; i < outStream.length; i++) { + outShortStream[i] = (short) (32767 * outStream[i]); + } + + if(mAudioTrack != null){ + try{ + mAudioTrack.write(outShortStream, 0, outShortStream.length); + mAudioTrack.play(); + } + catch(IllegalStateException e) + { + Log.i("PROCESS", e.getMessage()); + } + } + } + + public void setMode(EuOption.ModeType modeType) { + this.modeType = modeType; + txCore.setMode(modeType); + } + /* * @deprecated Replaced by {@link #setCode()}, deprecated for naming issue */ @Deprecated - public void process() { play(1); } + public void process() { play(1, PlayerEngine.ANDROID_DEFAULT_ENGINE); } /* * @deprecated Replaced by {@link #setCode()}, deprecated for naming issue */ @Deprecated - public void process(int count) { play(count); } + public void process(int count) { play(count, PlayerEngine.ANDROID_DEFAULT_ENGINE); } public void stop() { - txCore.setToneOn(false); - new Handler(Looper.getMainLooper()).postDelayed(() -> { - txCore.stop(); - },300); + if(modeType == EuOption.ModeType.DEFAULT && playerEngineType == PlayerEngine.ANDROID_DEFAULT_ENGINE) { + if(mAudioTrack != null) + mAudioTrack.pause(); + } + else { + txCore.setToneOn(false); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + txCore.stop(); + }, 300); + } } } From 45fa2acbd881da0209dc608b584e9b9170e824d9 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 19:38:18 +0900 Subject: [PATCH 29/31] Added 2 test-units for `packetWithFSKTest`, `waveRendererTest` > Those test-units are passed. --- .../src/main/cpp/tests/packetWithFSKTest.cpp | 63 +++++++++++++++++++ .../src/main/cpp/tests/waveRendererTest.cpp | 56 +++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 euphony/src/main/cpp/tests/packetWithFSKTest.cpp create mode 100644 euphony/src/main/cpp/tests/waveRendererTest.cpp diff --git a/euphony/src/main/cpp/tests/packetWithFSKTest.cpp b/euphony/src/main/cpp/tests/packetWithFSKTest.cpp new file mode 100644 index 0000000..03a5ca6 --- /dev/null +++ b/euphony/src/main/cpp/tests/packetWithFSKTest.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class PacketWithFSKTestFixture : public ::testing::TestWithParam { + +public: + void createFSK() { + EXPECT_EQ(fsk, nullptr); + fsk = new FSK(); + ASSERT_NE(fsk, nullptr); + } + + FSK* fsk = nullptr; + Packet* pkt = nullptr; +}; + +TEST_P(PacketWithFSKTestFixture, PacketFSKTest) +{ + createFSK(); + + string source; + string expectedResult; + + std::tie(source, expectedResult) = GetParam(); + HexVector hv = ASCIICharset().encode(source); + pkt = new Packet(hv); + + string actualResultCode = pkt->toString(); + EXPECT_EQ(actualResultCode, expectedResult); + + auto modulateResult = fsk->modulate(pkt->getPayloadStr()); + EXPECT_EQ(modulateResult.size(), source.size() * 2); + + auto demodulateResult = fsk->demodulate(modulateResult); + + EXPECT_EQ(expectedResult, demodulateResult->toString()); + pkt->clear(); +} + +INSTANTIATE_TEST_CASE_P( + PacketFSKTestSuite, + PacketWithFSKTestFixture, + ::testing::Values( + TestParamType("a", "S6197"), + TestParamType("b", "S6284"), + TestParamType("c", "S6375"), + TestParamType("abc", "S61626386"), + TestParamType("lmno", "S6c6d6e6f20"), + TestParamType("efg", "S656667c2"), + TestParamType("abcdefghijklmnopqrstuvwxyz", "S6162636465666768696a6b6c6d6e6f707172737475767778797aaa"), + TestParamType("ABC", "S414243e4"), + TestParamType("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "S4142434445464748494a4b4c4d4e4f505152535455565758595aea") +)); diff --git a/euphony/src/main/cpp/tests/waveRendererTest.cpp b/euphony/src/main/cpp/tests/waveRendererTest.cpp new file mode 100644 index 0000000..74eb9dc --- /dev/null +++ b/euphony/src/main/cpp/tests/waveRendererTest.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace Euphony; + +typedef std::tuple TestParamType; + +class WaveRendererTestFixture : public ::testing::TestWithParam { + +public: + std::shared_ptr wave = nullptr; + std::unique_ptr waveRenderer = nullptr; +}; + +TEST_P(WaveRendererTestFixture, WaveBuilderUnitTest) +{ + std::string inputString; + int expectedBufferSize; + int expectedStartData; + + std::tie(inputString, expectedBufferSize, expectedStartData) = GetParam(); + + auto fsk = new FSK(); + auto waveList = fsk->modulate(inputString); + + waveRenderer = std::make_unique(waveList, 2); + const auto actualWaveSourceSize = waveRenderer->getWaveSourceSize(); + EXPECT_EQ(actualWaveSourceSize, expectedBufferSize); + + auto demodulateResult = fsk->demodulate(waveRenderer->getWaveSource(), actualWaveSourceSize, kBufferSize); + EXPECT_EQ(inputString, demodulateResult->getPayloadStr()); + + delete fsk; +} + +INSTANTIATE_TEST_SUITE_P( + WaveRendererTest, + WaveRendererTestFixture, + ::testing::Values( + TestParamType("0", 2048, 0), + TestParamType("1", 2048, 1), + TestParamType("2", 2048, 2), + TestParamType("3", 2048, 3), + TestParamType("4", 2048, 4), + TestParamType("5", 2048, 5), + TestParamType("012345", 2048*6, 0), + TestParamType("0123456789", 2048*10, 0), + TestParamType("abcdef", 2048*6, 10), + TestParamType("0123456789abcdef", 2048*16, 0) + ) +); \ No newline at end of file From dc67bf865d03025dabc5f35264dfb17f25412127 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 19:38:55 +0900 Subject: [PATCH 30/31] Some hyper-parameter (ChannelCount & BufferFadeLength) --- euphony/src/main/cpp/core/Definitions.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/euphony/src/main/cpp/core/Definitions.h b/euphony/src/main/cpp/core/Definitions.h index cfe4c1e..e7ab6eb 100644 --- a/euphony/src/main/cpp/core/Definitions.h +++ b/euphony/src/main/cpp/core/Definitions.h @@ -8,11 +8,11 @@ namespace Euphony { typedef std::vector> WaveList; - constexpr int32_t kChannelCount = 2; + constexpr int32_t kChannelCount = 1; constexpr int32_t kSampleRate = 44100; constexpr int32_t kFFTSize = 512; constexpr int32_t kBufferSize = 2048; - constexpr int32_t kBufferFadeLength = 256; + constexpr int32_t kBufferFadeLength = 128; constexpr int32_t kFrequencyInterval = kSampleRate / kFFTSize; constexpr int32_t kStandardFrequency = 18001; constexpr int32_t kStartSignalFrequency = kStandardFrequency - kFrequencyInterval; From 5e89ae6bfa4cb32a0c33c3447f8b201b3d593ba6 Mon Sep 17 00:00:00 2001 From: designe Date: Mon, 13 Sep 2021 20:57:54 +0900 Subject: [PATCH 31/31] Added Unit-test(packetWithFSKTest, waveRenderTest) on CMakeLists.txt > minor refactoring on TxEngine --- euphony/src/main/cpp/core/source/TxEngine.cpp | 10 ++++++---- euphony/src/main/cpp/tests/CMakeLists.txt | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/euphony/src/main/cpp/core/source/TxEngine.cpp b/euphony/src/main/cpp/core/source/TxEngine.cpp index b099497..675ea84 100644 --- a/euphony/src/main/cpp/core/source/TxEngine.cpp +++ b/euphony/src/main/cpp/core/source/TxEngine.cpp @@ -21,7 +21,7 @@ class TxEngine::TxEngineImpl : public IRestartable{ std::shared_ptr mStream; oboe::AudioStreamBuilder mStreamBuilder; std::unique_ptr mCallback; - shared_ptr mAudioSource = nullptr; + std::shared_ptr mAudioSource = nullptr; bool mIsLatencyDetectionSupported = false; double eupiFreq; @@ -71,9 +71,7 @@ class TxEngine::TxEngineImpl : public IRestartable{ return std::make_shared(modulationResult, kChannelCount); } case ModeType::EUPI: - mAudioSource = std::make_shared(kSampleRate, kChannelCount); - std::dynamic_pointer_cast(mAudioSource)->setFrequency(eupiFreq); - return mAudioSource; + return std::make_shared(kSampleRate, kChannelCount); } } @@ -159,10 +157,14 @@ class TxEngine::TxEngineImpl : public IRestartable{ mModeType = ModeType::DEFAULT; break; case 1: + if(mModeType == ModeType::EUPI) + return; + mModeType = ModeType::EUPI; break; } + mAudioSource = createAudioSource(mModeType); } diff --git a/euphony/src/main/cpp/tests/CMakeLists.txt b/euphony/src/main/cpp/tests/CMakeLists.txt index f870510..d5c9eba 100644 --- a/euphony/src/main/cpp/tests/CMakeLists.txt +++ b/euphony/src/main/cpp/tests/CMakeLists.txt @@ -20,8 +20,10 @@ add_executable( packetTest.cpp packetBuilderTest.cpp packetErrorDetectorTest.cpp + packetWithFSKTest.cpp waveTest.cpp waveBuilderTest.cpp + waveRendererTest.cpp ) target_link_libraries(${TEST_EUPHONY} PUBLIC euphony gtest)