Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class SettingsAudioFormatFragment : SettingsBaseFragment() {

private val encoding by lazy { findPreference<ListPreference>(getString(R.string.key_audioformat_encoding)) }
private val bufferSize by lazy { findPreference<MaterialSeekbarPreference>(getString(R.string.key_audioformat_buffersize)) }
private val allowLowSamples by lazy { findPreference<MaterialSwitchPreference>(getString(R.string.key_audioformat_allow_low_samples)) }
private val legacyMode by lazy { findPreference<MaterialSwitchPreference>(getString(R.string.key_audioformat_processing)) }
private val enhancedMode by lazy { findPreference<MaterialSwitchPreference>(getString(R.string.key_audioformat_enhanced_processing)) }
private val enhancedModeInfo by lazy { findPreference<Preference>(getString(R.string.key_audioformat_enhanced_processing_info)) }
Expand Down Expand Up @@ -107,22 +108,48 @@ class SettingsAudioFormatFragment : SettingsBaseFragment() {
}

bufferSize?.setDefaultValue(preferences.getDefault<Float>(R.string.key_audioformat_buffersize))
applyBufferSizeRange(preferences.get<Boolean>(R.string.key_audioformat_allow_low_samples))
bufferSize?.setOnPreferenceChangeListener { _, newValue ->
if((newValue as Float) <= 1024){
val requestedValue = newValue as Float
val allowLow = preferences.get<Boolean>(R.string.key_audioformat_allow_low_samples)
if(!allowLow && requestedValue < Constants.AUDIO_BUFFER_MIN_DEFAULT) {
bufferSize?.setValue(Constants.AUDIO_BUFFER_MIN_DEFAULT.toFloat())
context?.sendLocalBroadcast(Intent(Constants.ACTION_SERVICE_HARD_REBOOT_CORE))
return@setOnPreferenceChangeListener false
}

if(requestedValue <= 1024){
requireContext().toast(R.string.audio_format_buffer_size_warning_low_value, false)
}
context?.sendLocalBroadcast(Intent(Constants.ACTION_SERVICE_HARD_REBOOT_CORE))
true
}
allowLowSamples?.setOnPreferenceChangeListener { _, newValue ->
applyBufferSizeRange(newValue as Boolean)
context?.sendLocalBroadcast(Intent(Constants.ACTION_SERVICE_HARD_REBOOT_CORE))
true
}
encoding?.setOnPreferenceChangeListener { _, _ ->
context?.sendLocalBroadcast(Intent(Constants.ACTION_SERVICE_HARD_REBOOT_CORE))
true
}
}

private fun applyBufferSizeRange(allowLow: Boolean) {
if (allowLow) {
return
}

val currentValue = preferences.get<Float>(R.string.key_audioformat_buffersize)
if(currentValue < Constants.AUDIO_BUFFER_MIN_DEFAULT) {
bufferSize?.setValue(Constants.AUDIO_BUFFER_MIN_DEFAULT.toFloat())
context?.sendLocalBroadcast(Intent(Constants.ACTION_SERVICE_HARD_REBOOT_CORE))
}
}

companion object {
fun newInstance(): SettingsAudioFormatFragment {
return SettingsAudioFormatFragment()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,17 @@ class RootlessAudioProcessorService : BaseAudioProcessorService() {
val encoding = AudioEncoding.fromInt(
preferences.get<String>(R.string.key_audioformat_encoding).toIntOrNull() ?: 1
)
val bufferSize = preferences.get<Float>(R.string.key_audioformat_buffersize).toInt()
val allowLowSamples = preferences.get<Boolean>(R.string.key_audioformat_allow_low_samples)
val minBufferSize = if (allowLowSamples) {
Constants.AUDIO_BUFFER_MIN_LOW
} else {
Constants.AUDIO_BUFFER_MIN_DEFAULT
}
val requestedBufferSize = preferences.get<Float>(R.string.key_audioformat_buffersize).toInt()
var bufferSize = maxOf(requestedBufferSize, minBufferSize)
if ((bufferSize % 2) != 0) {
bufferSize += 1
}
val bufferSizeBytes = when (encoding) {
AudioEncoding.PcmFloat -> bufferSize * Float.SIZE_BYTES
else -> bufferSize * Short.SIZE_BYTES
Expand All @@ -434,7 +444,8 @@ class RootlessAudioProcessorService : BaseAudioProcessorService() {
val sampleRate = clamp(determineSamplingRate(), 44100, 48000)

Timber.i("Sample rate: $sampleRate; Encoding: ${encoding.name}; " +
"Buffer size: $bufferSize; Buffer size (bytes): $bufferSizeBytes ; " +
"Buffer size: $bufferSize (requested: $requestedBufferSize); " +
"Buffer size (bytes): $bufferSizeBytes ; " +
"HAL buffer size (bytes): ${determineBufferSize()}")

// Create recorder and track
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ object Constants {
const val DEFAULT_GEQ_INTERNAL = "GraphicEQ: 0.0 0.0;"
const val DEFAULT_EQ = "31.0;62.0;125.0;250.0;500.0;1000.0;2000.0;4000.0;8000.0;16000.0;17000.0;18000.0;19000.0;20000.0;22000.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0"

// Audio format bounds (samples per channel)
const val AUDIO_BUFFER_MIN_DEFAULT = 128
const val AUDIO_BUFFER_MIN_LOW = 2

// Intent actions
const val ACTION_PREFERENCES_UPDATED = BuildConfig.APPLICATION_ID + ".action.preferences.UPDATED"
const val ACTION_SAMPLE_RATE_UPDATED = BuildConfig.APPLICATION_ID + ".action.sample_rate.UPDATED"
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values/defaults.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<bool name="default_powersave_suspend" translatable="false">true</bool>
<string name="default_audioformat_encoding" translatable="false">1</string>
<integer name="default_audioformat_buffersize" translatable="false">8192</integer>
<bool name="default_audioformat_allow_low_samples" translatable="false">false</bool>
<bool name="default_audioformat_processing" translatable="false">true</bool>
<bool name="default_audioformat_enhanced_processing" translatable="false">false</bool>
<bool name="default_audioformat_optimization_benchmark" translatable="false">false</bool>
Expand All @@ -55,4 +56,4 @@
<!-- Script editor -->
<integer name="default_editor_font_size" translatable="false" tools:keep="@integer/default_editor_font_size">13</integer>

</resources>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<string name="key_powersave_suspend" translatable="false">powersave_suspend</string>
<string name="key_audioformat_encoding" translatable="false">audioformat_encoding</string>
<string name="key_audioformat_buffersize" translatable="false">audioformat_buffersize</string>
<string name="key_audioformat_allow_low_samples" translatable="false">audioformat_allow_low_samples</string>
<string name="key_audioformat_processing" translatable="false">audioformat_processing</string>
<string name="key_audioformat_enhanced_processing" translatable="false">audioformat_enhanced_processing</string>
<string name="key_audioformat_optimization_benchmark" translatable="false">audioformat_optimization_benchmark</string>
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@
<string name="audio_format_buffer_size">Buffer size</string>
<string name="audio_format_buffer_size_unit">&#xa0;samples</string>
<string name="audio_format_buffer_size_warning_low_value">Warning: Low buffer sizes may cause audio issues such as clipping!</string>
<string name="audio_format_allow_low_samples">Allow low buffer sizes (&lt;128 samples)</string>
<string name="audio_format_allow_low_samples_summary">Experimental. Allows down to 2 samples with finer steps. May cause dropouts, crackling, or instability.</string>
<string name="audio_format_optimization_header">Convolver module optimizations</string>
<string name="audio_format_optimization_refresh">Refresh benchmarking data</string>
<string name="audio_format_optimization_benchmark">Use benchmarks to optimize performance</string>
Expand Down
12 changes: 9 additions & 3 deletions app/src/main/res/xml/app_audio_format_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,23 @@
<me.timschneeberger.rootlessjamesdsp.preference.MaterialSeekbarPreference
android:key="@string/key_audioformat_buffersize"
android:title="@string/audio_format_buffer_size"
app:seekBarIncrement="128"
app:seekBarIncrement="2"
app:precision="0"
app:unit="@string/audio_format_buffer_size_unit"
app:labelMinWidth="120dp"
app:minValue="128"
app:minValue="2"
app:maxValue="16384"
app:defaultValue="@integer/default_audioformat_buffersize"
app:allowDividerAbove="false"
app:updatesContinuously="false"
app:showSeekBarValue="true"
app:iconSpaceReserved="false"/>
Comment on lines +16 to 26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find the XML file to confirm the hardcoded values
find . -name "app_audio_format_preferences.xml" -type f

Repository: JSJ-Experiments/RootlessJamesDSP

Length of output: 131


🏁 Script executed:

# Find the SettingsAudioFormatFragment to verify the clamping logic
find . -name "SettingsAudioFormatFragment.kt" -type f

Repository: JSJ-Experiments/RootlessJamesDSP

Length of output: 180


🏁 Script executed:

# Search for MaterialSeekbarPreference implementation
find . -name "*MaterialSeekbar*" -o -name "*Seekbar*" | grep -E "\.(kt|java)$"

Repository: JSJ-Experiments/RootlessJamesDSP

Length of output: 171


🏁 Script executed:

# Read the XML file to confirm the hardcoded values
cat -n ./app/src/main/res/xml/app_audio_format_preferences.xml

Repository: JSJ-Experiments/RootlessJamesDSP

Length of output: 4868


🏁 Script executed:

# Read the SettingsAudioFormatFragment to check for change listeners and clamping
wc -l ./app/src/main/java/me/timschneeberger/rootlessjamesdsp/fragment/settings/SettingsAudioFormatFragment.kt

Repository: JSJ-Experiments/RootlessJamesDSP

Length of output: 184


🏁 Script executed:

# Read the MaterialSeekbarPreference to see if minValue can be set dynamically
cat -n ./app/src/main/java/me/timschneeberger/rootlessjamesdsp/preference/MaterialSeekbarPreference.kt

Repository: JSJ-Experiments/RootlessJamesDSP

Length of output: 21634


🏁 Script executed:

# Read the SettingsAudioFormatFragment to find the change listener and applyBufferSizeRange method
cat -n ./app/src/main/java/me/timschneeberger/rootlessjamesdsp/fragment/settings/SettingsAudioFormatFragment.kt

Repository: JSJ-Experiments/RootlessJamesDSP

Length of output: 8635


UI allows selecting invalid buffer sizes when "Allow low buffer sizes" is disabled.

The seekbar is configured with minValue="2" in XML, but when the toggle is off, the fragment's change listener (lines 115-118 in SettingsAudioFormatFragment.kt) clamps values below 128. Users can slide to invalid values like 64 only to have them silently adjusted to 128.

The applyBufferSizeRange() method currently only clamps the stored value but doesn't update the seekbar's visible minimum. Since MaterialSeekbarPreference exposes a public setMin() method that triggers UI updates via notifyChanged(), you should call it to reflect the effective range:

♻️ Update applyBufferSizeRange() to dynamically set seekbar minimum
private fun applyBufferSizeRange(allowLow: Boolean) {
    val minValue = if (allowLow) {
        Constants.AUDIO_BUFFER_MIN_LOW.toFloat()
    } else {
        Constants.AUDIO_BUFFER_MIN_DEFAULT.toFloat()
    }
    
    bufferSize?.setMin(minValue)
    
    val currentValue = preferences.get<Float>(R.string.key_audioformat_buffersize)
    if(currentValue < minValue) {
        bufferSize?.setValue(minValue)
        context?.sendLocalBroadcast(Intent(Constants.ACTION_SERVICE_HARD_REBOOT_CORE))
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/res/xml/app_audio_format_preferences.xml` around lines 16 - 26,
The seekbar still allows sliding to values below the effective minimum because
applyBufferSizeRange() only clamps stored values; update
applyBufferSizeRange(allowLow: Boolean) to call bufferSize?.setMin(...) (using
Constants.AUDIO_BUFFER_MIN_LOW or Constants.AUDIO_BUFFER_MIN_DEFAULT) so the UI
minimum updates, then read the current value via preferences.get(...) and if
it's below the new min call bufferSize?.setValue(...) and send the reboot
broadcast Intent(Constants.ACTION_SERVICE_HARD_REBOOT_CORE); this uses
MaterialSeekbarPreference.setMin(), bufferSize, preferences.get,
bufferSize.setValue, and the ACTION_SERVICE_HARD_REBOOT_CORE constant.

<me.timschneeberger.rootlessjamesdsp.preference.MaterialSwitchPreference
android:key="@string/key_audioformat_allow_low_samples"
android:defaultValue="@bool/default_audioformat_allow_low_samples"
android:title="@string/audio_format_allow_low_samples"
android:summary="@string/audio_format_allow_low_samples_summary"
app:iconSpaceReserved="false" />
</PreferenceCategory>

<PreferenceCategory
Expand Down Expand Up @@ -73,4 +79,4 @@
app:iconSpaceReserved="false"/>

</PreferenceCategory>
</PreferenceScreen>
</PreferenceScreen>
Loading
Loading