commit fac1b155624cac76b171f18be32ea2b1798bc43c parent 2012c9b1c2b25f1410b828b1b890456c4d64075b Author: Martin Ashby <martin@ashbysoft.com> Date: Sun, 5 Apr 2026 08:40:31 +0100 Remove tests; they don't work any longer Update app name, README.md and screenshots Diffstat:
23 files changed, 8 insertions(+), 483 deletions(-)
diff --git a/README.md b/README.md @@ -1,4 +1,7 @@ -# Cythara +# Plectrum + +Forked from [Cythara](github.com/gstraube/cythara) by [gstraube](github.com/gstraube) + A musical instrument tuner for Android. [<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" @@ -12,12 +15,6 @@ A musical instrument tuner for Android. * Displays deviations between -60 and 60 cents. * Supports scientific pitch notation and Solfège. -## Tests - -Run `./gradlew test` to run all unit tests. In addition, there are UI tests based on image comparisons which -can be run using `./gradlew connectedCheck`. The reference images are generated using a Nexus 5X emulator -(resolution: 1080 x 1920, 420 dpi) with API level 26. - ## Libraries The Tarsos DSP library (https://github.com/JorenSix/TarsosDSP) is used for pitch detection. @@ -26,7 +23,7 @@ Current library version: commit [d958352](https://github.com/JorenSix/TarsosDSP/ ## License -Cythara is licensed under GPLv3. A copy of the license is included in the [LICENSE](https://github.com/gstraube/cythara/blob/master/LICENSE). +Plectrum is licensed under GPLv3. A copy of the license is included in the [LICENSE](/LICENSE). # Contributors diff --git a/app/build.gradle b/app/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion 36 defaultConfig { - applicationId "com.github.cythara" + applicationId "net.mfashby.plectrum" minSdkVersion 21 targetSdkVersion 36 versionCode 27 @@ -50,12 +50,4 @@ dependencies { // Other implementation 'com.jaredrummler:material-spinner:1.3.1' implementation 'io.github.ShawnLin013:number-picker:2.4.13' - - // Testing libraries - testImplementation 'junit:junit:4.13' - testImplementation 'org.hamcrest:hamcrest:2.2' - testImplementation 'org.powermock:powermock-api-mockito2:2.0.7' - testImplementation 'org.powermock:powermock-module-junit4:2.0.7' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test:rules:1.2.0' } diff --git a/app/src/androidTest/java/com/github/cythara/TunerViewTest.java b/app/src/androidTest/java/com/github/cythara/TunerViewTest.java @@ -1,153 +0,0 @@ -package com.github.cythara; - -import android.Manifest; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.os.Environment; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.ActivityTestRule; -import androidx.test.rule.GrantPermissionRule; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import static com.github.cythara.tuning.GuitarTuning.Pitch.*; -import static java.lang.String.format; - -@RunWith(AndroidJUnit4.class) -public class TunerViewTest { - - @Rule - public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>( - MainActivity.class); - - @Rule - public GrantPermissionRule recordAudioRule = - GrantPermissionRule.grant(Manifest.permission.RECORD_AUDIO); - - @Rule - public GrantPermissionRule writePermissionRule = - GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE); - - @Rule - public GrantPermissionRule readPermissionRule = - GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE); - - @Test - public void exactly_matching_pitch_is_displayed() throws IOException { - isDisplayedCorrectly(R.drawable.exact, "exact", new PitchDifference(E4, 0)); - } - - @Test - public void close_match_is_displayed_correctly() throws IOException { - isDisplayedCorrectly(R.drawable.close, "close", new PitchDifference(G3, 2.4)); - } - - @Test - public void exact_deviations_are_displayed_correctly() throws IOException { - Map<Integer, DrawableResource> deviationToReferenceId = new HashMap<>(); - - deviationToReferenceId.put(-30, new DrawableResource(R.drawable.negative_30_cents, - "negative_30_cents")); - deviationToReferenceId.put(-20, new DrawableResource(R.drawable.negative_20_cents, - "negative_20_cents")); - deviationToReferenceId.put(-10, new DrawableResource(R.drawable.negative_10_cents, - "negative_10_cents")); - deviationToReferenceId.put(10, new DrawableResource(R.drawable.positive_10_cents, - "positive_10_cents")); - deviationToReferenceId.put(20, new DrawableResource(R.drawable.positive_20_cents, - "positive_20_cents")); - deviationToReferenceId.put(30, new DrawableResource(R.drawable.positive_30_cents, - "positive_30_cents")); - - for (Integer deviation : deviationToReferenceId.keySet()) { - DrawableResource drawableResource = deviationToReferenceId.get(deviation); - isDisplayedCorrectly(drawableResource.id, drawableResource.name, - new PitchDifference(B3, deviation)); - } - } - - @Test - public void non_exact_deviations_are_displayed_correctly() throws IOException { - Map<Double, DrawableResource> deviationToReferenceId = new HashMap<>(); - - deviationToReferenceId.put(-6.4, new DrawableResource(R.drawable.negative_10_cents, - "negative_10_cents")); - deviationToReferenceId.put(15.41, new DrawableResource(R.drawable.positive_20_cents, - "positive_20_cents")); - deviationToReferenceId.put(5.1, new DrawableResource(R.drawable.positive_10_cents, - "positive_10_cents")); - deviationToReferenceId.put(-27.32, new DrawableResource(R.drawable.negative_30_cents, - "negative_30_cents")); - deviationToReferenceId.put(4.7, new DrawableResource(R.drawable.positive_10_cents, - "positive_10_cents")); - deviationToReferenceId.put(29.5, new DrawableResource(R.drawable.positive_30_cents, - "positive_30_cents")); - - for (Double deviation : deviationToReferenceId.keySet()) { - DrawableResource drawableResource = deviationToReferenceId.get(deviation); - isDisplayedCorrectly(drawableResource.id, drawableResource.name, - new PitchDifference(B3, deviation)); - } - } - - @Test - public void values_outside_of_boundaries_are_not_displayed() throws IOException { - isDisplayedCorrectly(R.drawable.blank, "blank", - new PitchDifference(D3, 65)); - isDisplayedCorrectly(R.drawable.blank, "blank", - new PitchDifference(D3, -70)); - } - - public void isDisplayedCorrectly(int referenceId, String fileName, - PitchDifference pitchDifference) - throws IOException { - MainActivity mainActivity = mActivityRule.getActivity(); - - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inScaled = false; - Bitmap reference = BitmapFactory.decodeResource(mainActivity.getResources(), - referenceId, options); - - Bitmap generated = Bitmap.createBitmap(2048, 1024, Bitmap.Config.ARGB_4444); - Canvas canvas = new Canvas(generated); - TunerView tunerView = mainActivity.findViewById(R.id.pitch); - tunerView.setPitchDifference(pitchDifference); - - tunerView.draw(canvas); - - writeToFile(generated, format("%s.png", fileName)); - - Assert.assertTrue(reference.sameAs(generated)); - } - - private void writeToFile(Bitmap bitmap, String name) throws IOException { - File sdCard = Environment.getExternalStorageDirectory(); - try (FileOutputStream out = new FileOutputStream(sdCard.getAbsolutePath() + "/" + name)) { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - } - - private static class DrawableResource { - int id; - String name; - - DrawableResource(int id, String name) { - this.id = id; - this.name = name; - } - } -} diff --git a/app/src/androidTest/res/drawable/blank.png b/app/src/androidTest/res/drawable/blank.png Binary files differ. diff --git a/app/src/androidTest/res/drawable/close.png b/app/src/androidTest/res/drawable/close.png Binary files differ. diff --git a/app/src/androidTest/res/drawable/exact.png b/app/src/androidTest/res/drawable/exact.png Binary files differ. diff --git a/app/src/androidTest/res/drawable/negative_10_cents.png b/app/src/androidTest/res/drawable/negative_10_cents.png Binary files differ. diff --git a/app/src/androidTest/res/drawable/negative_20_cents.png b/app/src/androidTest/res/drawable/negative_20_cents.png Binary files differ. diff --git a/app/src/androidTest/res/drawable/negative_30_cents.png b/app/src/androidTest/res/drawable/negative_30_cents.png Binary files differ. diff --git a/app/src/androidTest/res/drawable/positive_10_cents.png b/app/src/androidTest/res/drawable/positive_10_cents.png Binary files differ. diff --git a/app/src/androidTest/res/drawable/positive_20_cents.png b/app/src/androidTest/res/drawable/positive_20_cents.png Binary files differ. diff --git a/app/src/androidTest/res/drawable/positive_30_cents.png b/app/src/androidTest/res/drawable/positive_30_cents.png Binary files differ. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ <resources xmlns:tools="http://schemas.android.com/tools"> - <string name="app_name">Cythara</string> + <string name="app_name">Plectrum</string> <string name="privacy_policy">Show privacy policy</string> <string name="privacy_policy_link" translatable="false"> diff --git a/app/src/test/java/com/github/cythara/PitchComparatorTest.java b/app/src/test/java/com/github/cythara/PitchComparatorTest.java @@ -1,46 +0,0 @@ -package com.github.cythara; - -import com.github.cythara.tuning.GuitarTuning; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import java.util.HashMap; -import java.util.Map; - -import static com.github.cythara.tuning.GuitarTuning.Pitch.*; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.closeTo; -import static org.junit.Assert.*; - -@RunWith(PowerMockRunner.class) -@PrepareForTest(MainActivity.class) -public class PitchComparatorTest { - - @Test - public void retrieveNote() { - PowerMockito.mockStatic(MainActivity.class); - Mockito.when(MainActivity.getCurrentTuning()).thenReturn(new GuitarTuning()); - Mockito.when(MainActivity.getReferencePitch()).thenReturn(440); - Mockito.when(MainActivity.isAutoModeEnabled()).thenReturn(true); - - Map<Float, PitchDifference> expectations = new HashMap<>(); - expectations.put(20f, new PitchDifference(E2, -2451.3202694972874)); - expectations.put(332f, new PitchDifference(E4, 12.415661386718076)); - expectations.put(197.67f, new PitchDifference(G3, 14.705999652460953)); - expectations.put(128.415f, new PitchDifference(D3, -232.0232233030192)); - - for (Float pitch : expectations.keySet()) { - PitchDifference actual = PitchComparator.retrieveNote(pitch); - PitchDifference expected = expectations.get(pitch); - - assertNotNull(expected); - assertThat(actual.closest, is(expected.closest)); - assertThat(actual.deviation, closeTo(expected.deviation, 0.01)); - } - } -} diff --git a/app/src/test/java/com/github/cythara/SamplerTest.java b/app/src/test/java/com/github/cythara/SamplerTest.java @@ -1,86 +0,0 @@ -package com.github.cythara; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; - -import static com.github.cythara.Sampler.*; -import static com.github.cythara.tuning.GuitarTuning.Pitch.B3; -import static com.github.cythara.tuning.GuitarTuning.Pitch.E2; -import static com.github.cythara.tuning.GuitarTuning.Pitch.G3; -import static org.hamcrest.CoreMatchers.either; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.closeTo; -import static org.junit.Assert.*; - -public class SamplerTest { - - @Test - public void the_average_difference_is_calculated_correctly() { - List<PitchDifference> samples = new ArrayList<>(); - - samples.add(new PitchDifference(E2, 2.46D)); - samples.add(new PitchDifference(E2, -10.3D)); - samples.add(new PitchDifference(E2, 5.71D)); - samples.add(new PitchDifference(E2, 12.532D)); - samples.add(new PitchDifference(E2, -0.414D)); - - PitchDifference pitchDifference = calculateAverageDifference(samples); - - double average = (2.46D - 10.3D + 5.71D + 12.532D - 0.414D) / 5D; - - assertNotNull(pitchDifference); - assertThat(pitchDifference.closest.getName(), is(E2.getName())); - assertThat(pitchDifference.deviation, closeTo(average, 0.001)); - } - - @Test - public void samples_are_filtered_correctly() { - List<PitchDifference> samples = new ArrayList<>(); - - samples.add(new PitchDifference(E2, 2D)); - samples.add(new PitchDifference(E2, 2D)); - samples.add(new PitchDifference(B3, 3D)); - samples.add(new PitchDifference(E2, 2D)); - samples.add(new PitchDifference(G3, 4D)); - samples.add(new PitchDifference(B3, 3D)); - - List<PitchDifference> filteredSamples = filterByNote(samples, B3); - - for (PitchDifference sample : filteredSamples) { - assertThat(sample.closest.getName(), is(B3.getName())); - } - } - - @Test - public void the_most_frequent_note_is_extracted_correctly() { - List<PitchDifference> samples = new ArrayList<>(); - - samples.add(new PitchDifference(E2, 2D)); - samples.add(new PitchDifference(E2, 2D)); - samples.add(new PitchDifference(B3, 3D)); - samples.add(new PitchDifference(E2, 2D)); - samples.add(new PitchDifference(G3, 4D)); - samples.add(new PitchDifference(B3, 3D)); - - Note note = extractMostFrequentNote(samples); - - assertThat(note.getName(), is(E2.getName())); - } - - @Test - public void if_there_are_notes_with_the_same_number_of_occurrences_one_of_them_is_returned() { - List<PitchDifference> samples = new ArrayList<>(); - - samples.add(new PitchDifference(G3, 2D)); - samples.add(new PitchDifference(E2, 2D)); - samples.add(new PitchDifference(B3, 3D)); - samples.add(new PitchDifference(E2, 2D)); - samples.add(new PitchDifference(B3, 3D)); - - Note note = extractMostFrequentNote(samples); - - assertThat(note.getName(), either(is(E2.getName())).or(is(B3.getName()))); - } -} diff --git a/app/src/test/java/com/github/cythara/tuning/NoteFrequencyCalculatorTest.java b/app/src/test/java/com/github/cythara/tuning/NoteFrequencyCalculatorTest.java @@ -1,70 +0,0 @@ -package com.github.cythara.tuning; - -import com.github.cythara.Note; -import com.github.cythara.NoteName; - -import org.junit.Assert; -import org.junit.Test; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -public class NoteFrequencyCalculatorTest { - - @Test - public void TestCalc() { - - InputStream resourceAsStream = getClass().getResourceAsStream("note_frequencies.csv"); - try (BufferedReader reader = - new BufferedReader(new InputStreamReader(resourceAsStream))) { - while (reader.ready()) { - String line = reader.readLine(); - String[] components = line.split(","); - String noteWithOctave = components[0].split("/")[0]; - String frequency = components[1]; - - String noteName = noteWithOctave.substring(0, 1); - String octave = noteWithOctave.substring(1); - String sign = ""; - if (noteWithOctave.contains("#")) { - noteName = noteWithOctave.substring(0, 1); - octave = noteWithOctave.substring(2); - sign = "#"; - } - - String finalNoteName = noteName; - String finalOctave = octave; - String finalSign = sign; - Note note = new Note() { - @Override - public NoteName getName() { - return NoteName.fromScientificName(finalNoteName); - } - - @Override - public int getOctave() { - return Integer.parseInt(finalOctave); - } - - @Override - public String getSign() { - return finalSign; - } - }; - - NoteFrequencyCalculator noteFrequencyCalculator = - new NoteFrequencyCalculator(440); - double expectedFrequency = Double.parseDouble(frequency); - double actualFrequency = noteFrequencyCalculator.getFrequency(note); - Assert.assertEquals(expectedFrequency, actualFrequency, 0.01); - - } - } catch (IOException e) { - Assert.fail(e.getMessage()); - } - - } - -} -\ No newline at end of file diff --git a/app/src/test/resources/com/github/cythara/tuning/note_frequencies.csv b/app/src/test/resources/com/github/cythara/tuning/note_frequencies.csv @@ -1,108 +0,0 @@ -C0,16.35,2109.89 -C#0/Db0,17.32,1991.47 -D0,18.35,1879.69 -D#0/Eb0,19.45,1774.20 -E0,20.60,1674.62 -F0,21.83,1580.63 -F#0/Gb0,23.12,1491.91 -G0,24.50,1408.18 -G#0/Ab0,25.96,1329.14 -A0,27.50,1254.55 -A#0/Bb0,29.14,1184.13 -B0,30.87,1117.67 -C1,32.70,1054.94 -C#1/Db1,34.65,995.73 -D1,36.71,939.85 -D#1/Eb1,38.89,887.10 -E1,41.20,837.31 -F1,43.65,790.31 -F#1/Gb1,46.25,745.96 -G1,49.00,704.09 -G#1/Ab1,51.91,664.57 -A1,55.00,627.27 -A#1/Bb1,58.27,592.07 -B1,61.74,558.84 -C2,65.41,527.47 -C#2/Db2,69.30,497.87 -D2,73.42,469.92 -D#2/Eb2,77.78,443.55 -E2,82.41,418.65 -F2,87.31,395.16 -F#2/Gb2,92.50,372.98 -G2,98.00,352.04 -G#2/Ab2,103.83,332.29 -A2,110.00,313.64 -A#2/Bb2,116.54,296.03 -B2,123.47,279.42 -C3,130.81,263.74 -C#3/Db3,138.59,248.93 -D3,146.83,234.96 -D#3/Eb3,155.56,221.77 -E3,164.81,209.33 -F3,174.61,197.58 -F#3/Gb3,185.00,186.49 -G3,196.00,176.02 -G#3/Ab3,207.65,166.14 -A3,220.00,156.82 -A#3/Bb3,233.08,148.02 -B3,246.94,139.71 -C4,261.63,131.87 -C#4/Db4,277.18,124.47 -D4,293.66,117.48 -D#4/Eb4,311.13,110.89 -E4,329.63,104.66 -F4,349.23,98.79 -F#4/Gb4,369.99,93.24 -G4,392.00,88.01 -G#4/Ab4,415.30,83.07 -A4,440.00,78.41 -A#4/Bb4,466.16,74.01 -B4,493.88,69.85 -C5,523.25,65.93 -C#5/Db5,554.37,62.23 -D5,587.33,58.74 -D#5/Eb5,622.25,55.44 -E5,659.25,52.33 -F5,698.46,49.39 -F#5/Gb5,739.99,46.62 -G5,783.99,44.01 -G#5/Ab5,830.61,41.54 -A5,880.00,39.20 -A#5/Bb5,932.33,37.00 -B5,987.77,34.93 -C6,1046.50,32.97 -C#6/Db6,1108.73,31.12 -D6,1174.66,29.37 -D#6/Eb6,1244.51,27.72 -E6,1318.51,26.17 -F6,1396.91,24.70 -F#6/Gb6,1479.98,23.31 -G6,1567.98,22.00 -G#6/Ab6,1661.22,20.77 -A6,1760.00,19.60 -A#6/Bb6,1864.66,18.50 -B6,1975.53,17.46 -C7,2093.00,16.48 -C#7/Db7,2217.46,15.56 -D7,2349.32,14.69 -D#7/Eb7,2489.02,13.86 -E7,2637.02,13.08 -F7,2793.83,12.35 -F#7/Gb7,2959.96,11.66 -G7,3135.96,11.00 -G#7/Ab7,3322.44,10.38 -A7,3520.00,9.80 -A#7/Bb7,3729.31,9.25 -B7,3951.07,8.73 -C8,4186.01,8.24 -C#8/Db8,4434.92,7.78 -D8,4698.63,7.34 -D#8/Eb8,4978.03,6.93 -E8,5274.04,6.54 -F8,5587.65,6.17 -F#8/Gb8,5919.91,5.83 -G8,6271.93,5.50 -G#8/Ab8,6644.88,5.19 -A8,7040.00,4.90 -A#8/Bb8,7458.62,4.63 -B8,7902.13,4.37 diff --git a/fastlane/Appfile b/fastlane/Appfile @@ -1,2 +1,2 @@ json_key_file "" # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one -package_name "com.github.cythara" # e.g. com.krausefx.app +package_name "net.mfashby.plectrum" # e.g. com.krausefx.app diff --git a/fastlane/metadata/android/en-US/phoneScreenshots/choose_frequency.png b/fastlane/metadata/android/en-US/phoneScreenshots/choose_frequency.png Binary files differ. diff --git a/fastlane/metadata/android/en-US/phoneScreenshots/choose_notation.png b/fastlane/metadata/android/en-US/phoneScreenshots/choose_notation.png Binary files differ. diff --git a/fastlane/metadata/android/en-US/phoneScreenshots/feedback.png b/fastlane/metadata/android/en-US/phoneScreenshots/feedback.png Binary files differ. diff --git a/fastlane/metadata/android/en-US/phoneScreenshots/listening.png b/fastlane/metadata/android/en-US/phoneScreenshots/listening.png Binary files differ. diff --git a/fastlane/metadata/android/en-US/phoneScreenshots/tunings.png b/fastlane/metadata/android/en-US/phoneScreenshots/tunings.png Binary files differ.