CMF Buds 2 Plus: Add spatial mode

- Add support for spatial mode
- Enable for Buds 2 Plus
This commit is contained in:
Dominik Riebeling
2026-05-24 09:32:36 +02:00
parent eaebdc8278
commit 9b00c3edaa
7 changed files with 52 additions and 2 deletions
@@ -379,7 +379,7 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_NOTHING_EAR1_ULTRA_BASS_LEVEL = "pref_nothing_ultra_bass_level";
public static final String PREF_HEADPHONES_EQUALIZER = "pref_headphones_equalizer";
public static final String PREF_HEADPHONES_LOW_LATENCY = "pref_headphones_low_latency";
public static final String PREF_NOTHING_EAR1_SPATIAL_AUDIO = "pref_nothing_spatial_audio";
public static final String PREF_HUAWEI_FREEBUDS_INEAR = "pref_freebuds_inear_detection";
public static final String PREF_HUAWEI_FREEBUDS_AUDIOMODE = "pref_freebuds_audiomode";
@@ -739,9 +739,10 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
addPreferenceHandlerFor(PREF_NOTHING_EAR1_INEAR);
addPreferenceHandlerFor(PREF_NOTHING_EAR1_AUDIOMODE);
addPreferenceHandlerFor(PREF_HEADPHONES_LOW_LATENCY);
addPreferenceHandlerFor(PREF_HEADPHONES_EQUALIZER);
addPreferenceHandlerFor(PREF_NOTHING_EAR1_ULTRA_BASS_ENABLED);
addPreferenceHandlerFor(PREF_NOTHING_EAR1_ULTRA_BASS_LEVEL);
addPreferenceHandlerFor(PREF_HEADPHONES_EQUALIZER);
addPreferenceHandlerFor(PREF_NOTHING_EAR1_SPATIAL_AUDIO);
addPreferenceHandlerFor(PREF_HUAWEI_FREEBUDS_INEAR);
addPreferenceHandlerFor(PREF_HUAWEI_FREEBUDS_AUDIOMODE);
@@ -81,6 +81,9 @@ public abstract class AbstractEarCoordinator extends AbstractBLClassicDeviceCoor
final List<Integer> touchOptions = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.TOUCH_OPTIONS);
touchOptions.add(R.xml.devicesettings_cmf_buds_touch_options);
}
if (supportsSpatialAudio()) {
audio.add(R.xml.devicesettings_nothing_spatial_audio);
}
if (supportsLowLatency()) {
final List<Integer> connection = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CONNECTION);
connection.add(R.xml.devicesettings_headphones_low_latency);
@@ -129,4 +132,6 @@ public abstract class AbstractEarCoordinator extends AbstractBLClassicDeviceCoor
public boolean supportsTouchOptions() {
return false;
}
public boolean supportsSpatialAudio() { return false; }
}
@@ -69,4 +69,8 @@ public class CmfBuds2PlusCoordinator extends AbstractEarCoordinator {
@Override
public boolean supportsLowLatency() { return true; }
@Override
public boolean supportsSpatialAudio() { return true; }
}
@@ -89,6 +89,9 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
if (getCoordinator().supportsTouchOptions()) {
sendCommand(builder, nothingProtocol.encodeTouchOptionsRequest());
}
if (getCoordinator().supportsSpatialAudio()) {
sendCommand(builder, nothingProtocol.encodeSpatialAudioReq());
}
return builder;
}
@@ -175,6 +178,10 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
"set touch options " + config,
nothingProtocol.encodeTouchOptions(config, prefs)
);
case DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_SPATIAL_AUDIO:
byte spatial = (byte)(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_SPATIAL_AUDIO, false) ? 0x01 : 0x00);
sendCommand("set spatial audio", nothingProtocol.encodeSpatialAudio(spatial));
break;
default:
LOG.debug("CONFIG: " + config);
@@ -211,6 +218,7 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
private static final short low_latency_status = (short) 0xc041;
private static final short equalizer_status = (short) 0xc050;
private static final short ultra_bass_status = (short) 0xc04e;
private static final short spatial_audio_status = (short) 0xc04f;
private static final short unk_maybe_ack = (short) 0xf002;
private static final short unk_close_case = (short) 0xe002; //sent twice when the case is closed with earphones in
@@ -223,6 +231,7 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
private static final short low_latency = (short) 0xf040;
private static final short equalizer = (short) 0xf01d;
private static final short ultra_bass = (short) 0xf051;
private static final short spatial_audio = (short) 0xf052;
private final boolean incrementCounter;
private int messageCounter = 0x00;
@@ -322,6 +331,9 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
devEvts.add(handleUltraBassStatus(payload));
}
break;
case spatial_audio_status:
devEvts.add(handleSpatialAudio(payload));
break;
case low_latency_status:
devEvts.add(handleLowLatency(payload));
break;
@@ -399,6 +411,10 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
return encodeMessage((short) 0x120, ultra_bass_status, new byte[]{});
}
byte[] encodeSpatialAudioReq() {
return encodeMessage((short) 0x120, spatial_audio_status, new byte[]{});
}
byte[] encodeInEarDetectionReq() {
return encodeMessage((short) 0x120, in_ear_detection_status, new byte[]{});
}
@@ -465,7 +481,14 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
final int level = Math.max(1, Math.min(5, ((payload[1] & 0xff) + 1) / 2));
preferencesEvent.withPreference(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_LEVEL, level);
}
return preferencesEvent;
}
private GBDeviceEventUpdatePreferences handleSpatialAudio(byte[] payload) {
final GBDeviceEventUpdatePreferences preferencesEvent = new GBDeviceEventUpdatePreferences();
if (payload.length >= 1) {
preferencesEvent.withPreference(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_SPATIAL_AUDIO, payload[0] == 0x01 ? true : false);
}
return preferencesEvent;
}
@@ -528,6 +551,10 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
return encodeMessage((short) 0x120, in_ear_detection, new byte[]{0x01, 0x01, enabled});
}
byte[] encodeSpatialAudio(byte enabled) {
return encodeMessage((short) 0x120, spatial_audio, new byte[]{enabled});
}
byte[] encodeAudioMode(String desired) {
int modeBitmask = NothingAudioMode.valueOf("off").getBitmask();
try {
+2
View File
@@ -3218,6 +3218,8 @@
<string name="nothing_equalizer_classical">Classical</string>
<string name="nothing_equalizer_custom">Custom</string>
<string name="nothing_equalizer_dirac">Dirac</string>
<string name="nothing_prefs_spatial_audio_title">Spatial Audio</string>
<string name="nothing_prefs_spatial_audio_summary">An immersive listening experience that makes you feel like you\'re part of the music, movie or game.</string>
<string name="prefs_equalizer_preset">Equalizer Preset</string>
<string name="pref_title_equalizer_normal">Normal</string>
<string name="pref_title_equalizer_bass_boost">Bass boost</string>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreferenceCompat
android:defaultValue="true"
android:icon="@drawable/ic_surround"
android:key="pref_nothing_spatial_audio"
android:layout="@layout/preference_checkbox"
android:summary="@string/nothing_prefs_spatial_audio_summary"
android:title="@string/nothing_prefs_spatial_audio_title" />
</androidx.preference.PreferenceScreen>