Skip to content

Commit fa6ecaf

Browse files
committed
Version 3.3.3
1 parent 05e0113 commit fa6ecaf

61 files changed

Lines changed: 6507 additions & 874 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ android {
3434
applicationId "de.schliweb.bluesharpbendingapp"
3535
minSdk 28
3636
targetSdk 35
37-
versionCode 62
37+
versionCode 63
3838
versionName project.version.toString()
3939

4040
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

android/app/src/main/java/de/schliweb/bluesharpbendingapp/app/MainActivity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ protected void onCreate(Bundle savedInstanceState) {
287287
microphoneSettingsViewHandler.initAlgorithmList();
288288
microphoneSettingsViewHandler.initMicrophoneList();
289289
microphoneSettingsViewHandler.initConfidenceList();
290+
microphoneSettingsViewHandler.initChordConfidenceList();
290291
noteSettingsViewHandler.initConcertPitchList();
291292
androidSettingsHandler.initLockScreen();
292293
}

android/app/src/main/java/de/schliweb/bluesharpbendingapp/model/mircophone/android/MicrophoneAndroid.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import de.schliweb.bluesharpbendingapp.utils.*;
3535

3636
import java.util.ArrayList;
37+
import java.util.List;
3738
import java.util.concurrent.BlockingQueue;
3839
import java.util.concurrent.ExecutorService;
3940
import java.util.concurrent.Executors;
@@ -341,8 +342,15 @@ private void processAudioData(float[] buffer, int bytesRead) {
341342
double conf = 0;
342343
double pitch = -1;
343344
PitchDetector.PitchDetectionResult result;
344-
ChordDetectionResult chordResult = PitchDetector.detectChord(audioData, SAMPLE_RATE);
345+
ChordDetectionResult chordResult;
345346

347+
// Only perform chord detection if it's enabled
348+
if (isChordDetectionEnabled()) {
349+
chordResult = PitchDetector.detectChord(audioData, SAMPLE_RATE);
350+
} else {
351+
// Create an empty chord detection result when chord detection is disabled
352+
chordResult = new ChordDetectionResult(List.of(), 0.0);
353+
}
346354
// Use the utility class for pitch detection, passing the SAMPLE_RATE as a parameter
347355
if ("YIN".equals(getAlgorithm())) {
348356
result = PitchDetector.detectPitchYIN(audioData, SAMPLE_RATE);
@@ -352,9 +360,18 @@ private void processAudioData(float[] buffer, int bytesRead) {
352360
result = PitchDetector.detectPitchMPM(audioData, SAMPLE_RATE);
353361
pitch = result.pitch();
354362
conf = result.confidence();
363+
} else if ("HYBRID".equals(getAlgorithm())) {
364+
result = PitchDetector.detectPitchHybrid(audioData, SAMPLE_RATE);
365+
pitch = result.pitch();
366+
conf = result.confidence();
355367
}
356368

357369
if (conf < confidence) pitch = -1;
370+
371+
// Apply the chord confidence threshold to chord detection
372+
if(chordResult.confidence() < chordConfidence)
373+
chordResult = new ChordDetectionResult(List.of(), 0.0);
374+
358375
if (microphoneHandler != null) {
359376
microphoneHandler.handle(pitch, PitchDetector.calcRMS(audioData), chordResult); // frequency, RMS
360377
}

android/app/src/main/java/de/schliweb/bluesharpbendingapp/view/android/SettingsFragment.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
154154
setSelectedTune(model.getStoredTuneIndex());
155155
setSelectedAlgorithm(model.getStoredAlgorithmIndex());
156156
setSelectedConfidence(model.getStoredConfidenceIndex());
157+
setSelectedChordConfidence(model.getStoredChordConfidenceIndex());
157158
setSelectedLockScreen(model.getStoredLockScreenIndex());
158159
setSelectedShowChord(model.getStoredShowChordIndex());
159160
androidSettingsViewHandler.handleLockScreenSelection(model.getStoredLockScreenIndex());
@@ -311,6 +312,34 @@ public void setVolume(double volume) {
311312
// on android do nothing
312313
}
313314

315+
@Override
316+
public void setSelectedChordConfidence(int i) {
317+
Spinner spinner = binding.settingsChordConfList;
318+
spinner.setSelection(i);
319+
}
320+
321+
@Override
322+
public void setChordConfidences(String[] chordConfidences) {
323+
// Safeguard in case keys is null
324+
if (chordConfidences == null) {
325+
chordConfidences = new String[0]; // Use an empty array
326+
}
327+
Spinner spinner = binding.settingsChordConfList;
328+
ArrayAdapter<String> adapter = new ArrayAdapter<>(this.requireContext(), R.layout.spinner_list, chordConfidences);
329+
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
330+
spinner.setAdapter(adapter);
331+
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
332+
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
333+
microphoneSettingsViewHandler.handleChordConfidenceSelection((int) id);
334+
}
335+
336+
public void onNothingSelected(AdapterView<?> parent) {
337+
// no need
338+
}
339+
});
340+
341+
}
342+
314343

315344
@Override
316345
public Object getInstance() {

android/app/src/main/res/layout-land/fragment_settings.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@
312312
<LinearLayout
313313
android:layout_width="match_parent"
314314
android:layout_height="wrap_content"
315+
android:layout_marginBottom="@dimen/settings_item_margin"
315316
android:orientation="horizontal">
316317

317318
<TextView
@@ -331,6 +332,30 @@
331332
android:minHeight="48dp"
332333
android:spinnerMode="dropdown" />
333334
</LinearLayout>
335+
336+
<!-- Chord Confidence Setting -->
337+
<LinearLayout
338+
android:layout_width="match_parent"
339+
android:layout_height="wrap_content"
340+
android:orientation="horizontal">
341+
342+
<TextView
343+
android:id="@+id/settings_chord_conf_label"
344+
android:layout_width="0dp"
345+
android:layout_height="wrap_content"
346+
android:layout_weight="1"
347+
android:text="@string/settings_chord_conf_label"
348+
android:textAppearance="?android:attr/textAppearanceMedium" />
349+
350+
<Spinner
351+
android:id="@+id/settings_chord_conf_list"
352+
style="@style/ModernSpinner"
353+
android:layout_width="0dp"
354+
android:layout_weight="1"
355+
android:layout_height="wrap_content"
356+
android:minHeight="48dp"
357+
android:spinnerMode="dropdown" />
358+
</LinearLayout>
334359
</LinearLayout>
335360
</com.google.android.material.card.MaterialCardView>
336361
</LinearLayout>

android/app/src/main/res/layout/fragment_settings.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@
199199
<LinearLayout
200200
android:layout_width="match_parent"
201201
android:layout_height="wrap_content"
202+
android:layout_marginBottom="@dimen/settings_item_margin"
202203
android:orientation="horizontal">
203204

204205
<TextView
@@ -218,6 +219,30 @@
218219
android:minHeight="48dp"
219220
android:spinnerMode="dropdown" />
220221
</LinearLayout>
222+
223+
<!-- Chord Confidence Setting -->
224+
<LinearLayout
225+
android:layout_width="match_parent"
226+
android:layout_height="wrap_content"
227+
android:orientation="horizontal">
228+
229+
<TextView
230+
android:id="@+id/settings_chord_conf_label"
231+
android:layout_width="0dp"
232+
android:layout_height="wrap_content"
233+
android:layout_weight="1"
234+
android:text="@string/settings_chord_conf_label"
235+
android:textAppearance="?android:attr/textAppearanceMedium" />
236+
237+
<Spinner
238+
android:id="@+id/settings_chord_conf_list"
239+
style="@style/ModernSpinner"
240+
android:layout_width="0dp"
241+
android:layout_height="wrap_content"
242+
android:layout_weight="1"
243+
android:minHeight="48dp"
244+
android:spinnerMode="dropdown" />
245+
</LinearLayout>
221246
</LinearLayout>
222247
</com.google.android.material.card.MaterialCardView>
223248

android/app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<string name="training_precision_label">Precision</string>
5151
<string name="training_progress_label">Progress</string>
5252
<string name="settings_conf_label">Confidence</string>
53+
<string name="settings_chord_conf_label">Chord Confidence</string>
5354

5455
<!-- Settings screen section headers -->
5556
<string name="settings_harp_header">Harp Settings</string>

base/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,4 @@ dependencies {
2828

2929
test {
3030
useJUnitPlatform() // Enables the use of JUnit 5 (JUnit Platform) for testing
31-
exclude '**/PitchDetectorComparisonTest.class'
3231
}

base/src/main/java/de/schliweb/bluesharpbendingapp/controller/HarpController.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public class HarpController implements HarpSettingsViewHandler, HarpViewHandler,
7373
private Harmonica harmonica;
7474
private NoteContainer[] noteContainers;
7575
private final ChordAndDetectionResultComparator chordComparator = new ChordAndDetectionResultComparator();
76+
private MicrophoneController microphoneController;
7677

7778
/**
7879
* Constructs a new HarpController with the specified dependencies.
@@ -98,6 +99,18 @@ public HarpController(MainModel model, ModelStorageService modelStorageService,
9899
LoggingUtils.logInitialized("HarpController");
99100
}
100101

102+
/**
103+
* Sets the MicrophoneController instance to be used by this HarpController.
104+
* This method is used to resolve the circular dependency between HarpController and MicrophoneController.
105+
*
106+
* @param microphoneController The MicrophoneController instance to use
107+
*/
108+
public void setMicrophoneController(MicrophoneController microphoneController) {
109+
LoggingContext.setComponent("HarpController");
110+
LoggingUtils.logDebug("Setting MicrophoneController instance");
111+
this.microphoneController = microphoneController;
112+
}
113+
101114
/**
102115
* Updates the harp view with the provided frequency and chord detection results.
103116
* This method resets note container states and processes either chord highlighting or
@@ -298,9 +311,22 @@ public void initTuneList() {
298311

299312
@Override
300313
public void handleShowChordSelection(int showChordIndex) {
314+
LoggingContext.setComponent("HarpController");
315+
LoggingUtils.logUserAction("Show chord selection", "showChordIndex=" + showChordIndex);
316+
301317
this.model.setSelectedShowChordIndex(showChordIndex);
302318
this.model.setStoredShowChordIndex(showChordIndex);
319+
320+
// Update the microphone's chord detection setting immediately
321+
if (microphoneController != null) {
322+
LoggingUtils.logDebug("Updating microphone chord detection setting");
323+
microphoneController.updateChordDetectionSetting();
324+
} else {
325+
LoggingUtils.logWarning("Cannot update microphone chord detection setting", "MicrophoneController is null");
326+
}
327+
303328
modelStorageService.storeModel(model);
329+
LoggingUtils.logOperationCompleted("Show chord selection handling");
304330
}
305331

306332
@Override

base/src/main/java/de/schliweb/bluesharpbendingapp/controller/MicrophoneController.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ public MicrophoneController(MainModel model, ModelStorageService modelStorageSer
7373
microphone.setAlgorithm(model.getStoredAlgorithmIndex());
7474
microphone.setName(model.getStoredMicrophoneIndex());
7575
microphone.setConfidence(model.getStoredConfidenceIndex());
76+
microphone.setChordConfidence(model.getStoredChordConfidenceIndex());
77+
78+
// Set chord detection enabled based on the "Show Chords" setting
79+
// If showChordIndex is 1, chord display is enabled
80+
microphone.setChordDetectionEnabled(model.getSelectedShowChordIndex() == 1);
81+
LoggingUtils.logDebug("Chord detection " + (model.getSelectedShowChordIndex() == 1 ? "enabled" : "disabled") +
82+
" based on Show Chords setting: " + model.getSelectedShowChordIndex());
7683

7784
LoggingUtils.logDebug("Registering microphone handler");
7885
microphone.setMicrophoneHandler(this);
@@ -227,6 +234,26 @@ public void initConfidenceList() {
227234
}
228235
}
229236

237+
@Override
238+
public void initChordConfidenceList() {
239+
LoggingContext.setComponent("MicrophoneController");
240+
LoggingUtils.logOperationStarted("Chord confidence list initialization");
241+
242+
if (window.isMicrophoneSettingsViewActive()) {
243+
MicrophoneSettingsView microphoneSettingsView = window.getMicrophoneSettingsView();
244+
245+
LoggingUtils.logDebug("Setting chord confidence list in the microphone settings view");
246+
microphoneSettingsView.setChordConfidences(AbstractMicrophone.getSupportedChordConfidences());
247+
248+
LoggingUtils.logDebug("Setting the selected chord confidence in the microphone settings view");
249+
microphoneSettingsView.setSelectedChordConfidence(model.getSelectedChordConfidenceIndex());
250+
251+
LoggingUtils.logOperationCompleted("Chord confidence list initialization");
252+
} else {
253+
LoggingUtils.logWarning("Chord confidence list initialization skipped", "Microphone settings view is not active");
254+
}
255+
}
256+
230257
@Override
231258
public void handleConfidenceSelection(int confidenceIndex) {
232259
LoggingContext.setComponent("MicrophoneController");
@@ -251,6 +278,30 @@ public void handleConfidenceSelection(int confidenceIndex) {
251278
LoggingUtils.logPerformance("Model storage", duration);
252279
}
253280

281+
@Override
282+
public void handleChordConfidenceSelection(int chordConfidenceIndex) {
283+
LoggingContext.setComponent("MicrophoneController");
284+
LoggingUtils.logUserAction("Chord confidence selection", "chordConfidenceIndex=" + chordConfidenceIndex);
285+
286+
LoggingUtils.logDebug("Updating stored chord confidence index in the model");
287+
this.model.setStoredChordConfidenceIndex(chordConfidenceIndex);
288+
this.model.setSelectedChordConfidenceIndex(chordConfidenceIndex);
289+
290+
if (microphone != null) {
291+
LoggingUtils.logDebug("Setting chord confidence index on the microphone", "chordConfidenceIndex=" + chordConfidenceIndex);
292+
microphone.setChordConfidence(chordConfidenceIndex);
293+
LoggingUtils.logOperationCompleted("Chord confidence selection handling");
294+
} else {
295+
LoggingUtils.logWarning("Cannot set chord confidence index", "Microphone object is null");
296+
}
297+
298+
LoggingUtils.logDebug("Storing updated model");
299+
long startTime = System.currentTimeMillis();
300+
modelStorageService.storeModel(model);
301+
long duration = System.currentTimeMillis() - startTime;
302+
LoggingUtils.logPerformance("Model storage", duration);
303+
}
304+
254305
/**
255306
* Updates the microphone settings view with the specified frequency.
256307
*
@@ -280,4 +331,21 @@ private void updateMicrophoneSettingsViewVolume(double volume) {
280331
microphoneSettingsView.setVolume(volume);
281332
}
282333
}
334+
335+
/**
336+
* Updates the microphone's chord detection setting based on the model's "Show Chords" setting.
337+
* This method should be called whenever the "Show Chords" setting is changed.
338+
*/
339+
public void updateChordDetectionSetting() {
340+
LoggingContext.setComponent("MicrophoneController");
341+
LoggingUtils.logOperationStarted("Updating chord detection setting");
342+
343+
boolean enableChordDetection = model.getSelectedShowChordIndex() == 1;
344+
microphone.setChordDetectionEnabled(enableChordDetection);
345+
346+
LoggingUtils.logDebug("Chord detection " + (enableChordDetection ? "enabled" : "disabled") +
347+
" based on Show Chords setting: " + model.getSelectedShowChordIndex());
348+
349+
LoggingUtils.logOperationCompleted("Chord detection setting update");
350+
}
283351
}

0 commit comments

Comments
 (0)