소스 검색

增加调音器module

Pq 2 년 전
부모
커밋
0da9cbdda8
29개의 변경된 파일682개의 추가작업 그리고 0개의 파일을 삭제
  1. 1 0
      musictuner/.gitignore
  2. 58 0
      musictuner/build.gradle
  3. BIN
      musictuner/libs/TarsosDSP-Android-latest.jar
  4. 21 0
      musictuner/proguard-rules.pro
  5. 26 0
      musictuner/src/androidTest/java/com/cooleshow/musictuner/ExampleInstrumentedTest.java
  6. 18 0
      musictuner/src/main/AndroidManifest.xml
  7. 69 0
      musictuner/src/main/java/com/cooleshow/musictuner/MusicTunerActivity.java
  8. 14 0
      musictuner/src/main/java/com/cooleshow/musictuner/bean/VoiceToneBean.java
  9. 135 0
      musictuner/src/main/java/com/cooleshow/musictuner/utils/VoiceDataUtils.java
  10. 30 0
      musictuner/src/main/res/drawable-v24/ic_launcher_foreground.xml
  11. 170 0
      musictuner/src/main/res/drawable/ic_launcher_background.xml
  12. 68 0
      musictuner/src/main/res/layout/activity_main.xml
  13. 5 0
      musictuner/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  14. 5 0
      musictuner/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  15. BIN
      musictuner/src/main/res/mipmap-hdpi/ic_launcher.webp
  16. BIN
      musictuner/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  17. BIN
      musictuner/src/main/res/mipmap-mdpi/ic_launcher.webp
  18. BIN
      musictuner/src/main/res/mipmap-mdpi/ic_launcher_round.webp
  19. BIN
      musictuner/src/main/res/mipmap-xhdpi/ic_launcher.webp
  20. BIN
      musictuner/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  21. BIN
      musictuner/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  22. BIN
      musictuner/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  23. BIN
      musictuner/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  24. BIN
      musictuner/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  25. 16 0
      musictuner/src/main/res/values-night/themes.xml
  26. 10 0
      musictuner/src/main/res/values/colors.xml
  27. 3 0
      musictuner/src/main/res/values/strings.xml
  28. 16 0
      musictuner/src/main/res/values/themes.xml
  29. 17 0
      musictuner/src/test/java/com/cooleshow/musictuner/ExampleUnitTest.java

+ 1 - 0
musictuner/.gitignore

@@ -0,0 +1 @@
+/build

+ 58 - 0
musictuner/build.gradle

@@ -0,0 +1,58 @@
+plugins {
+    id 'com.android.application'
+}
+
+android {
+    compileSdkVersion 32
+
+    defaultConfig {
+        applicationId "com.cooleshow.musictuner"
+        minSdkVersion 23
+        targetSdkVersion 32
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        debug{
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        preRelease{
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+
+
+    buildFeatures {
+        viewBinding = true
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation 'androidx.appcompat:appcompat:1.3.0'
+    implementation 'com.google.android.material:material:1.4.0'
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+    testImplementation 'junit:junit:4.13.2'
+    implementation project(':BaseLibrary')
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}

BIN
musictuner/libs/TarsosDSP-Android-latest.jar


+ 21 - 0
musictuner/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
musictuner/src/androidTest/java/com/cooleshow/musictuner/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.cooleshow.musictuner;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("com.cooleshow.musictuner", appContext.getPackageName());
+    }
+}

+ 18 - 0
musictuner/src/main/AndroidManifest.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.cooleshow.musictuner">
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.CooleShow">
+        <activity
+            android:name=".MusicTunerActivity"
+            android:exported="false">
+        </activity>
+    </application>
+
+</manifest>

+ 69 - 0
musictuner/src/main/java/com/cooleshow/musictuner/MusicTunerActivity.java

@@ -0,0 +1,69 @@
+package com.cooleshow.musictuner;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.viewbinding.ViewBinding;
+import be.tarsos.dsp.AudioDispatcher;
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+import be.tarsos.dsp.io.android.AudioDispatcherFactory;
+import be.tarsos.dsp.pitch.PitchDetectionHandler;
+import be.tarsos.dsp.pitch.PitchDetectionResult;
+import be.tarsos.dsp.pitch.PitchProcessor;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+
+import com.cooleshow.base.ui.activity.BaseActivity;
+import com.cooleshow.musictuner.bean.VoiceToneBean;
+import com.cooleshow.musictuner.databinding.ActivityMainBinding;
+import com.cooleshow.musictuner.utils.VoiceDataUtils;
+
+public class MusicTunerActivity extends BaseActivity<ActivityMainBinding> {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        test();
+    }
+
+    @Override
+    protected void initView() {
+
+    }
+
+    @Override
+    protected ActivityMainBinding getLayoutView() {
+        return ActivityMainBinding.inflate(getLayoutInflater());
+    }
+
+    private void test(){
+        AudioDispatcher dispatcher =
+                AudioDispatcherFactory.fromDefaultMicrophone(22050, 1024, 0);
+        PitchDetectionHandler pdh = new PitchDetectionHandler() {
+            @Override
+            public void handlePitch(PitchDetectionResult res, AudioEvent e) {
+                final float pitchInHz = res.getPitch();
+                final float probability = res.getProbability();
+                VoiceToneBean voiceToneBean = VoiceDataUtils.getInstance().searchTarget(pitchInHz);
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+//                        mTvResult.setText(pitchInHz + "Hz");
+//                        if (voiceToneBean != null) {
+//                            mTvResultVoiceTones.setText(voiceToneBean.name);
+//                            mBeforeResultVoiceTones.setText(voiceToneBean.beforeName);
+//                            mAfterResultVoiceTones.setText(voiceToneBean.afterName);
+//                            mTvDifference.setText(voiceToneBean.difference);
+//                        }
+                    }
+                });
+//                Log.i("pq", "pitchInHz:" + pitchInHz);
+//                Log.i("pq", "probability:" + probability);
+            }
+        };
+        AudioProcessor p = new PitchProcessor(PitchProcessor.PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, pdh);
+        dispatcher.addAudioProcessor(p);
+        new Thread(dispatcher, "Audio Dispatcher").start();
+    }
+}

+ 14 - 0
musictuner/src/main/java/com/cooleshow/musictuner/bean/VoiceToneBean.java

@@ -0,0 +1,14 @@
+package com.cooleshow.musictuner.bean;
+
+/**
+ * Author by pq, Date on 2022/10/13.
+ */
+public class VoiceToneBean {
+    public String name;
+    public double voiceFrequencyValue;
+    public String difference;//与当前标本音的差值
+    public String beforeName;//前一个音的名称
+    public double beforeVoiceFrequencyValue;//前一个音的音频值
+    public String afterName;//后一个音的名称
+    public double afterVoiceFrequencyValue;//后一个音的音频值
+}

+ 135 - 0
musictuner/src/main/java/com/cooleshow/musictuner/utils/VoiceDataUtils.java

@@ -0,0 +1,135 @@
+package com.cooleshow.musictuner.utils;
+
+import android.util.Log;
+
+import com.cooleshow.musictuner.bean.VoiceToneBean;
+
+import java.util.ArrayList;
+
+/**
+ * Author by pq, Date on 2022/10/13.
+ */
+public class VoiceDataUtils {
+    public static volatile VoiceDataUtils instance;
+    public static final String[] VOICE_OF_TONE = new String[]{"C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B"};
+    public static final double[] ALL_VOICE_SAMPLES = new double[]{16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87,
+            32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74,
+            65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.8, 110.0, 116.5, 123.5,
+            130.8, 138.6, 146.8, 155.6, 164.8, 174.6, 185.0, 196.0, 207.7, 220.0, 233.1, 246.9,
+            261.6, 277.2, 293.7, 311.1, 329.6, 349.2, 370.0, 392.0, 415.3, 440.0, 466.2, 493.9,
+            523.3, 554.4, 587.3, 622.3, 659.3, 698.5, 740.0, 784.0, 830.6, 880.0, 932.3, 987.8,
+            1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
+            2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
+            4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902};
+    public ArrayList<VoiceToneBean> voiceToneBeans;
+
+    private VoiceDataUtils() {
+        buildData();
+    }
+
+    public static VoiceDataUtils getInstance() {
+        if (instance == null) {
+            synchronized (VoiceDataUtils.class) {
+                if (instance == null) {
+                    instance = new VoiceDataUtils();
+                }
+            }
+        }
+        return instance;
+    }
+
+    public void buildData() {
+        voiceToneBeans = new ArrayList<>();
+        for (int i = 0; i < VOICE_OF_TONE.length; i++) {
+            for (int j = 0; j < 9; j++) {
+                VoiceToneBean voiceToneBean = new VoiceToneBean();
+                voiceToneBean.name = VOICE_OF_TONE[i] + j;
+                int position = i + j * 12;//0(0+0*12) 12(0+1*12) 24(0+2*12)
+                voiceToneBean.voiceFrequencyValue = ALL_VOICE_SAMPLES[position];
+                voiceToneBeans.add(voiceToneBean);
+            }
+        }
+        Log.i("pq", "voiceToneBeans:" + voiceToneBeans.size());
+        for (int i = 0; i < voiceToneBeans.size(); i++) {
+            VoiceToneBean voiceToneBean = voiceToneBeans.get(i);
+            Log.i("pq", "data name:" + voiceToneBean.name);
+            Log.i("pq", "data voiceFrequencyValue:" + voiceToneBean.voiceFrequencyValue);
+        }
+    }
+
+    public VoiceToneBean searchTarget(float targetVoiceFrequencyValue) {
+        if (targetVoiceFrequencyValue == -1) {
+            return null;
+        }
+        if (targetVoiceFrequencyValue > ALL_VOICE_SAMPLES[ALL_VOICE_SAMPLES.length - 1]) {
+            VoiceToneBean voiceToneBean = buildBean(ALL_VOICE_SAMPLES.length - 2, ALL_VOICE_SAMPLES.length - 3, ALL_VOICE_SAMPLES.length - 1);
+            voiceToneBean.difference = String.valueOf(targetVoiceFrequencyValue - ALL_VOICE_SAMPLES[ALL_VOICE_SAMPLES.length - 2]);
+            return voiceToneBean;
+        }
+        if (targetVoiceFrequencyValue < ALL_VOICE_SAMPLES[0]) {
+            VoiceToneBean voiceToneBean = buildBean(1, 0, 2);
+            voiceToneBean.difference = String.valueOf(targetVoiceFrequencyValue - ALL_VOICE_SAMPLES[1]);
+            return voiceToneBean;
+        }
+        int index = firstIndex(ALL_VOICE_SAMPLES, targetVoiceFrequencyValue);
+        int start = index - 1;
+        int end = index;
+        int resultPosition = start;
+        Log.i("qaz", "targetVoiceFrequencyValue:" + targetVoiceFrequencyValue);
+        Log.i("qaz", "start:" + ALL_VOICE_SAMPLES[start]);
+        Log.i("qaz", "end:" + ALL_VOICE_SAMPLES[end]);
+        double startDiff = Math.abs(ALL_VOICE_SAMPLES[start] - targetVoiceFrequencyValue);
+        double endDiff = Math.abs(ALL_VOICE_SAMPLES[end] - targetVoiceFrequencyValue);
+
+        if (startDiff <= endDiff) {
+            resultPosition = start;
+        } else {
+            resultPosition = end;
+        }
+        VoiceToneBean voiceToneBean = buildBean(resultPosition, resultPosition - 1, resultPosition + 1);
+        voiceToneBean.difference = String.valueOf(targetVoiceFrequencyValue - ALL_VOICE_SAMPLES[resultPosition]);
+        return voiceToneBean;
+    }
+
+    private VoiceToneBean buildBean(int pos, int beforePos, int afterPos) {
+        VoiceToneBean voiceToneBean = new VoiceToneBean();
+        voiceToneBean.name = countName(pos);
+        voiceToneBean.voiceFrequencyValue = ALL_VOICE_SAMPLES[pos];
+        voiceToneBean.beforeName = countName(beforePos);
+        voiceToneBean.beforeVoiceFrequencyValue = ALL_VOICE_SAMPLES[beforePos];
+        voiceToneBean.afterName = countName(afterPos);
+        voiceToneBean.afterVoiceFrequencyValue = ALL_VOICE_SAMPLES[afterPos];
+        Log.i("qaz", "result name:" + voiceToneBean.name +
+                "\nvalue:" + voiceToneBean.voiceFrequencyValue);
+        return voiceToneBean;
+    }
+
+    private String countName(int pos) {
+        int row = pos / 12;
+        int frequencyValuePosition = (pos % 12);
+        String result = VOICE_OF_TONE[frequencyValuePosition] + row;
+        return result;
+    }
+
+    private int firstIndex(double[] A, double target) {
+        int start = 0, end = A.length - 1;
+        while (start + 1 < end) {
+            int mid = start + (end - start) / 2;
+            if (A[mid] < target) {
+                start = mid;
+            } else if (A[mid] > target) {
+                end = mid;
+            } else {
+                end = mid;
+            }
+        }
+
+        if (A[start] >= target) {
+            return start;
+        }
+        if (A[end] >= target) {
+            return end;
+        }
+        return A.length;
+    }
+}

+ 30 - 0
musictuner/src/main/res/drawable-v24/ic_launcher_foreground.xml

@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="85.84757"
+                android:endY="92.4963"
+                android:startX="42.9492"
+                android:startY="49.59793"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>

+ 170 - 0
musictuner/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>

+ 68 - 0
musictuner/src/main/res/layout/activity_main.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MusicTunerActivity">
+
+    <TextView
+        android:id="@+id/tv_result"
+        android:layout_marginTop="100dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        tools:text="haha"
+        android:textColor="#000000"
+        android:textSize="30sp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        app:layout_constraintTop_toBottomOf="@+id/tv_result"
+        android:id="@+id/tv_result_voice_tones"
+        android:layout_marginTop="100dp"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        tools:text="C"
+        android:textColor="#000000"
+        android:textSize="40sp"
+        android:textStyle="bold"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        app:layout_constraintBaseline_toBaselineOf="@+id/tv_result_voice_tones"
+        android:id="@+id/tv_before_result_voice_tones"
+        app:layout_constraintLeft_toLeftOf="parent"
+        tools:text="C"
+        android:textColor="#000000"
+        android:textSize="20sp"
+        android:textStyle="bold"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        app:layout_constraintBaseline_toBaselineOf="@+id/tv_result_voice_tones"
+        android:id="@+id/tv_after_result_voice_tones"
+        app:layout_constraintRight_toRightOf="parent"
+        tools:text="C"
+        android:textColor="#000000"
+        android:textSize="20sp"
+        android:textStyle="bold"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        tools:text="C"
+        android:layout_marginBottom="10dp"
+        app:layout_constraintRight_toRightOf="@+id/tv_result_voice_tones"
+        app:layout_constraintLeft_toLeftOf="@+id/tv_result_voice_tones"
+        app:layout_constraintBottom_toTopOf="@+id/tv_result_voice_tones"
+        android:textColor="#000000"
+        android:textSize="20sp"
+        android:id="@+id/tv_difference"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 5 - 0
musictuner/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

+ 5 - 0
musictuner/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

BIN
musictuner/src/main/res/mipmap-hdpi/ic_launcher.webp


BIN
musictuner/src/main/res/mipmap-hdpi/ic_launcher_round.webp


BIN
musictuner/src/main/res/mipmap-mdpi/ic_launcher.webp


BIN
musictuner/src/main/res/mipmap-mdpi/ic_launcher_round.webp


BIN
musictuner/src/main/res/mipmap-xhdpi/ic_launcher.webp


BIN
musictuner/src/main/res/mipmap-xhdpi/ic_launcher_round.webp


BIN
musictuner/src/main/res/mipmap-xxhdpi/ic_launcher.webp


BIN
musictuner/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp


BIN
musictuner/src/main/res/mipmap-xxxhdpi/ic_launcher.webp


BIN
musictuner/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp


+ 16 - 0
musictuner/src/main/res/values-night/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.CooleShow" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_200</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/black</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_200</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 10 - 0
musictuner/src/main/res/values/colors.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>

+ 3 - 0
musictuner/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">musicTuner</string>
+</resources>

+ 16 - 0
musictuner/src/main/res/values/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.CooleShow" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_500</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/white</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_700</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 17 - 0
musictuner/src/test/java/com/cooleshow/musictuner/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.cooleshow.musictuner;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}