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
27 changes: 10 additions & 17 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,26 +197,19 @@ jobs:
echo
echo "## Downloads"
echo
echo "| Variant | Architecture | Download | Asset |"
echo "| --- | --- | --- | --- |"
echo "| Variant | Architecture | Download |"
echo "| --- | --- | --- |"

for variant in rootless root; do
if [ "$variant" = "rootless" ]; then
variant_label="RootlessJamesDSP"
variant_label="RootlessJamesDSP"
for arch in universal arm64-v8a armeabi-v7a x86_64 x86; do
asset_name="$(jq -r --arg arch "$arch" '.assets[] | select(.name | test("-rootless-full-" + $arch + "-release-signed\\.apk$")) | .name' assets.json | head -n 1)"

if [ -n "$asset_name" ] && [ "$asset_name" != "null" ]; then
download_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${asset_name}"
echo "| ${variant_label} | ${arch} | [Download](${download_url}) |"
else
variant_label="JamesDSP (root)"
echo "| ${variant_label} | ${arch} | N/A |"
fi

for arch in universal arm64-v8a armeabi-v7a x86_64 x86; do
url="$(jq -r --arg variant "$variant" --arg arch "$arch" '.assets[] | select(.name | test("-" + $variant + "-full-" + $arch + "-release-signed\\.apk$")) | .url' assets.json | head -n 1)"

if [ -n "$url" ] && [ "$url" != "null" ]; then
asset_name="${url##*/}"
echo "| ${variant_label} | ${arch} | [Download](${url}) | \`${asset_name}\` |"
else
echo "| ${variant_label} | ${arch} | N/A | - |"
fi
done
done
} > release_downloads.md

Expand Down
2 changes: 2 additions & 0 deletions app/src/main/cpp/libjamesdsp-wrapper/JamesDspWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,7 @@ Java_me_timschneeberger_rootlessjamesdsp_interop_JamesDspWrapper_setSpectrumExte
jfloat strengthLinear,
jint referenceFreq,
jfloat wetMix,
jboolean wetOnlyMonitor,
jfloat postGainDb,
jboolean safetyEnabled,
jfloat hpQ,
Expand Down Expand Up @@ -1015,6 +1016,7 @@ Java_me_timschneeberger_rootlessjamesdsp_interop_JamesDspWrapper_setSpectrumExte
safeStrength,
safeReferenceFreq,
safeWetMix,
wetOnlyMonitor ? 1 : 0,
safePostGainDb,
safetyEnabled ? 1 : 0,
safeHpQ,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.preference.Preference.SummaryProvider
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import androidx.recyclerview.widget.RecyclerView
import me.timschneeberger.rootlessjamesdsp.R
import me.timschneeberger.rootlessjamesdsp.activity.GraphicEqualizerActivity
Expand Down Expand Up @@ -248,19 +249,53 @@ class PreferenceGroupFragment : PreferenceFragmentCompat(), KoinComponent {
val unitPref = findPreference<ListPreference>(getString(R.string.key_spectrum_ext_strength_unit))
val strengthPercentPref = findPreference<MaterialSeekbarPreference>(getString(R.string.key_spectrum_ext_strength_percent))
val strengthDbPref = findPreference<MaterialSeekbarPreference>(getString(R.string.key_spectrum_ext_strength_db))
val allowBoostPref = findPreference<SwitchPreferenceCompat>(getString(R.string.key_spectrum_ext_allow_boost))
val harmonicsPref = findPreference<EditTextPreference>(getString(R.string.key_spectrum_ext_harmonics))

bindStrengthUnitPreferences(
unitPref = unitPref,
strengthPercentPref = strengthPercentPref,
strengthDbPref = strengthDbPref,
maxDb = 0.0f,
maxDbProvider = {
if (allowBoostPref?.isChecked == true) {
SPECTRUM_STRENGTH_BOOST_DB_MAX
} else {
SPECTRUM_STRENGTH_DB_DEFAULT_MAX
}
},
minPercent = 0.0f,
maxPercent = 100.0f,
maxPercentProvider = {
if (allowBoostPref?.isChecked == true) {
strengthPercentFromDb(SPECTRUM_STRENGTH_BOOST_DB_MAX)
} else {
100.0f
}
},
minLinear = 0.0f,
mapMinDbToZero = true
)

fun applyBoostUi(isEnabled: Boolean) {
val maxDb = if (isEnabled) SPECTRUM_STRENGTH_BOOST_DB_MAX else SPECTRUM_STRENGTH_DB_DEFAULT_MAX
val maxPercent = if (isEnabled) strengthPercentFromDb(SPECTRUM_STRENGTH_BOOST_DB_MAX) else 100.0f

strengthDbPref?.setMax(maxDb)
strengthPercentPref?.setMax(maxPercent)

if ((strengthDbPref?.getValue() ?: maxDb) > maxDb) {
strengthDbPref?.setValue(maxDb)
}
if ((strengthPercentPref?.getValue() ?: maxPercent) > maxPercent) {
strengthPercentPref?.setValue(maxPercent)
}
}

applyBoostUi(allowBoostPref?.isChecked == true)
allowBoostPref?.setOnPreferenceChangeListener { _, newValue ->
applyBoostUi(newValue as Boolean)
true
}

harmonicsPref?.setOnPreferenceChangeListener { _, newValue ->
val harmonicsRaw = (newValue as? String)?.trim().orEmpty()
if (isValidSemicolonDelimitedHarmonics(harmonicsRaw)) {
Expand All @@ -279,9 +314,9 @@ class PreferenceGroupFragment : PreferenceFragmentCompat(), KoinComponent {
unitPref = unitPref,
strengthPercentPref = strengthPercentPref,
strengthDbPref = strengthDbPref,
maxDb = CLARITY_STRENGTH_DB_MAX,
maxDbProvider = { CLARITY_STRENGTH_DB_MAX },
minPercent = 0.0f,
maxPercent = 800.0f,
maxPercentProvider = { 800.0f },
minLinear = 0.0f,
mapMinDbToZero = true
)
Expand Down Expand Up @@ -322,39 +357,40 @@ class PreferenceGroupFragment : PreferenceFragmentCompat(), KoinComponent {
unitPref: ListPreference?,
strengthPercentPref: MaterialSeekbarPreference?,
strengthDbPref: MaterialSeekbarPreference?,
maxDb: Float,
maxDbProvider: () -> Float,
minPercent: Float,
maxPercent: Float,
maxPercentProvider: () -> Float,
minLinear: Float = 0.01f,
mapMinDbToZero: Boolean = false,
) {
val maxLinear = dbToLinear(maxDb)
val minDb = MIN_STRENGTH_DB
val percentUnit = requireContext().getString(R.string.strength_unit_value_percent)
val dbUnit = requireContext().getString(R.string.strength_unit_value_db)
var internalUpdate = false

fun percentToLinear(percent: Float): Float {
val maxLinear = dbToLinear(maxDbProvider())
return clampLinear(percent / 100.0f, minLinear, maxLinear)
}

fun dbToLinearMapped(db: Float): Float {
if (mapMinDbToZero && db <= minDb) {
return 0.0f
}
val maxLinear = dbToLinear(maxDbProvider())
return clampLinear(dbToLinear(db), minLinear, maxLinear)
}

fun linearToDbMapped(linear: Float): Float {
if (mapMinDbToZero && linear <= 0.0f) {
return minDb
}
return linearToDb(linear).coerceIn(minDb, maxDb)
return linearToDb(linear).coerceIn(minDb, maxDbProvider())
}

strengthPercentPref?.setOnPreferenceChangeListener { _, newValue ->
if (internalUpdate) return@setOnPreferenceChangeListener true
val percent = (newValue as Float).coerceIn(minPercent, maxPercent)
val percent = (newValue as Float).coerceIn(minPercent, maxPercentProvider())
val linear = percentToLinear(percent)
val db = linearToDbMapped(linear)
internalUpdate = true
Expand All @@ -365,9 +401,9 @@ class PreferenceGroupFragment : PreferenceFragmentCompat(), KoinComponent {

strengthDbPref?.setOnPreferenceChangeListener { _, newValue ->
if (internalUpdate) return@setOnPreferenceChangeListener true
val db = (newValue as Float).coerceIn(minDb, maxDb)
val db = (newValue as Float).coerceIn(minDb, maxDbProvider())
val linear = dbToLinearMapped(db)
val percent = (linear * 100.0f).coerceIn(minPercent, maxPercent)
val percent = (linear * 100.0f).coerceIn(minPercent, maxPercentProvider())
internalUpdate = true
strengthPercentPref?.setValue(percent)
internalUpdate = false
Expand All @@ -384,7 +420,7 @@ class PreferenceGroupFragment : PreferenceFragmentCompat(), KoinComponent {
} else if (newUnit == percentUnit) {
val db = strengthDbPref?.getValue() ?: minDb
val linear = dbToLinearMapped(db)
strengthPercentPref?.setValue((linear * 100.0f).coerceIn(minPercent, maxPercent))
strengthPercentPref?.setValue((linear * 100.0f).coerceIn(minPercent, maxPercentProvider()))
}
internalUpdate = false
setStrengthVisibility(newUnit, strengthPercentPref, strengthDbPref)
Expand Down Expand Up @@ -413,6 +449,8 @@ class PreferenceGroupFragment : PreferenceFragmentCompat(), KoinComponent {

private fun dbToLinear(db: Float): Float = 10.0.pow((db / 20.0f).toDouble()).toFloat()

private fun strengthPercentFromDb(db: Float): Float = dbToLinear(db) * 100.0f

/**
* Domain-specific clamping for linear gain values used by [linearToDb] and [dbToLinear].
* Inputs and bounds are linear-domain amplitudes and must stay positive for log conversion.
Expand Down Expand Up @@ -460,6 +498,8 @@ class PreferenceGroupFragment : PreferenceFragmentCompat(), KoinComponent {
private const val CROSSFEED_MODE_DEFAULT_VALUE = "5"
private const val CUSTOM_CROSSFEED_MODE_VALUE = "99"
private const val MIN_STRENGTH_DB = -40.0f
private const val SPECTRUM_STRENGTH_DB_DEFAULT_MAX = 0.0f
private const val SPECTRUM_STRENGTH_BOOST_DB_MAX = 12.0f
private const val CLARITY_STRENGTH_LINEAR_MAX = 8.0f
private val CLARITY_STRENGTH_DB_MAX = (20.0 * log10(CLARITY_STRENGTH_LINEAR_MAX.toDouble())).toFloat()
// Semicolon-separated decimal numbers used by Spectrum Extension harmonics list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ abstract class JamesDspBaseEngine(val context: Context, val callbacks: JamesDspW
private const val SPECTRUM_STRENGTH_UNIT_PERCENT = "percent"
private const val SPECTRUM_STRENGTH_UNIT_DB = "db"
private const val SPECTRUM_STRENGTH_DB_MIN = -40.0f
private const val SPECTRUM_STRENGTH_DB_MAX = 0.0f
private const val SPECTRUM_STRENGTH_DB_DEFAULT_MAX = 0.0f
private const val SPECTRUM_STRENGTH_DB_BOOST_MAX = 12.0f
private const val SPECTRUM_STRENGTH_PERCENT_MIN = 0.0f
private const val SPECTRUM_STRENGTH_PERCENT_MAX = 100.0f
private const val SPECTRUM_HARMONICS_DEFAULT_RAW = "0.02;0;0.02;0;0.02;0;0.02;0;0.02;0"
private const val MAX_EQ_INTERPOLATION_MODE = 1
private val DEFAULT_SPECTRUM_HARMONICS = doubleArrayOf(0.02, 0.0, 0.02, 0.0, 0.02, 0.0, 0.02, 0.0, 0.02, 0.0)
private val SPECTRUM_STRENGTH_PERCENT_BOOST_MAX = 10.0.pow((SPECTRUM_STRENGTH_DB_BOOST_MAX / 20.0f).toDouble()).toFloat() * SPECTRUM_STRENGTH_PERCENT_MAX
}

abstract var enabled: Boolean
Expand Down Expand Up @@ -128,8 +130,10 @@ abstract class JamesDspBaseEngine(val context: Context, val callbacks: JamesDspW
val spectrumStrengthUnit = cache.get(R.string.key_spectrum_ext_strength_unit, SPECTRUM_STRENGTH_UNIT_PERCENT)
val spectrumStrengthPercent = cache.get(R.string.key_spectrum_ext_strength_percent, 10f)
val spectrumStrengthDb = cache.get(R.string.key_spectrum_ext_strength_db, -20f)
val spectrumAllowBoost = cache.get(R.string.key_spectrum_ext_allow_boost, false)
val spectrumRefFreq = cache.get(R.string.key_spectrum_ext_ref_freq, 7600f).toInt()
val spectrumWetMix = cache.get(R.string.key_spectrum_ext_wet_mix, 100f)
val spectrumWetOnlyMonitor = cache.get(R.string.key_spectrum_ext_wet_only_monitor, false)
val spectrumPostGain = cache.get(R.string.key_spectrum_ext_post_gain, 0f)
val spectrumSafety = cache.get(R.string.key_spectrum_ext_safety, false)
val spectrumHpQ = cache.get(R.string.key_spectrum_ext_hp_q, 0.717f)
Expand Down Expand Up @@ -246,8 +250,10 @@ abstract class JamesDspBaseEngine(val context: Context, val callbacks: JamesDspW
spectrumStrengthUnit,
spectrumStrengthPercent,
spectrumStrengthDb,
spectrumAllowBoost,
spectrumRefFreq,
spectrumWetMix,
spectrumWetOnlyMonitor,
spectrumPostGain,
spectrumSafety,
spectrumHpQ,
Expand Down Expand Up @@ -491,17 +497,21 @@ abstract class JamesDspBaseEngine(val context: Context, val callbacks: JamesDspW
strengthUnit: String,
strengthPercent: Float,
strengthDb: Float,
allowBoost: Boolean,
referenceFreq: Int,
wetMixPercent: Float,
wetOnlyMonitor: Boolean,
postGainDb: Float,
safetyEnabled: Boolean,
hpQ: Float,
lpQ: Float,
lpCutoffOffsetHz: Int,
harmonicsRaw: String,
): Boolean {
val maxDb = if (allowBoost) SPECTRUM_STRENGTH_DB_BOOST_MAX else SPECTRUM_STRENGTH_DB_DEFAULT_MAX
val maxPercent = if (allowBoost) SPECTRUM_STRENGTH_PERCENT_BOOST_MAX else SPECTRUM_STRENGTH_PERCENT_MAX
val uiStrength = if (strengthUnit == SPECTRUM_STRENGTH_UNIT_DB) {
val clampedDb = strengthDb.coerceIn(SPECTRUM_STRENGTH_DB_MIN, SPECTRUM_STRENGTH_DB_MAX)
val clampedDb = strengthDb.coerceIn(SPECTRUM_STRENGTH_DB_MIN, maxDb)
// Map the minimum db value to full-off for stable round-tripping with percent mode.
if (clampedDb <= SPECTRUM_STRENGTH_DB_MIN) {
0.0f
Expand All @@ -510,7 +520,7 @@ abstract class JamesDspBaseEngine(val context: Context, val callbacks: JamesDspW
}
} else {
strengthPercent
}.coerceIn(SPECTRUM_STRENGTH_PERCENT_MIN, SPECTRUM_STRENGTH_PERCENT_MAX)
}.coerceIn(SPECTRUM_STRENGTH_PERCENT_MIN, maxPercent)

// Stock ViPER mapping: param65550 = trunc(strength * 5.6), core uses /100.
// The local JNI path applies Spectrum params as a single native call instead of discrete
Expand All @@ -524,6 +534,7 @@ abstract class JamesDspBaseEngine(val context: Context, val callbacks: JamesDspW
exciter,
referenceFreq,
wetMixPercent.coerceIn(0f, 100f) / 100.0f,
wetOnlyMonitor,
postGainDb.coerceIn(-24f, 24f),
safetyEnabled,
hpQ.coerceIn(0.1f, 3.0f),
Expand Down Expand Up @@ -791,6 +802,7 @@ abstract class JamesDspBaseEngine(val context: Context, val callbacks: JamesDspW
strengthLinear: Float,
referenceFreq: Int,
wetMix: Float,
wetOnlyMonitor: Boolean,
postGainDb: Float,
safetyEnabled: Boolean,
hpQ: Float,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ class JamesDspLocalEngine(context: Context, callbacks: JamesDspWrapper.JamesDspC
strengthLinear: Float,
referenceFreq: Int,
wetMix: Float,
wetOnlyMonitor: Boolean,
postGainDb: Float,
safetyEnabled: Boolean,
hpQ: Float,
Expand All @@ -236,6 +237,7 @@ class JamesDspLocalEngine(context: Context, callbacks: JamesDspWrapper.JamesDspC
strengthLinear,
referenceFreq,
wetMix,
wetOnlyMonitor,
postGainDb,
safetyEnabled,
hpQ,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ class JamesDspRemoteEngine(
strengthLinear: Float,
referenceFreq: Int,
wetMix: Float,
wetOnlyMonitor: Boolean,
postGainDb: Float,
safetyEnabled: Boolean,
hpQ: Float,
Expand All @@ -503,6 +504,9 @@ class JamesDspRemoteEngine(

if (enable) {
val fallbackToLegacy = {
if (wetOnlyMonitor) {
Timber.w("Spectrum wet-only monitor is unavailable in legacy protocol mode.")
}
val referenceResult = effect.setParameter(PARAM_SPECTRUM_EXTENSION_BARK, referenceFreq)
if (referenceResult != AudioEffect.SUCCESS) {
markUnsupported(referenceResult, true)
Expand Down Expand Up @@ -533,7 +537,7 @@ class JamesDspRemoteEngine(
hpQ,
lpQ,
lpCutoffOffsetHz.toFloat()
) + safeHarmonics.map { it.toFloat() }
) + safeHarmonics.map { it.toFloat() } + floatArrayOf(if (wetOnlyMonitor) 1.0f else 0.0f)
val payloadResult = effect.setParameterFloatArray(PARAM_SPECTRUM_EXTENSION, payload)

if (payloadResult == AudioEffect.SUCCESS) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ object JamesDspWrapper {
strengthLinear: Float,
referenceFreq: Int,
wetMix: Float,
wetOnlyMonitor: Boolean,
postGainDb: Float,
safetyEnabled: Boolean,
hpQ: Float,
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@
<string name="key_spectrum_ext_strength_unit" translatable="false">spectrum_ext_strength_unit</string>
<string name="key_spectrum_ext_strength_percent" translatable="false">spectrum_ext_strength_percent</string>
<string name="key_spectrum_ext_strength_db" translatable="false">spectrum_ext_strength_db</string>
<string name="key_spectrum_ext_allow_boost" translatable="false">spectrum_ext_allow_boost</string>
<string name="key_spectrum_ext_ref_freq" translatable="false">spectrum_ext_ref_freq</string>
<string name="key_spectrum_ext_wet_mix" translatable="false">spectrum_ext_wet_mix</string>
<string name="key_spectrum_ext_wet_only_monitor" translatable="false">spectrum_ext_wet_only_monitor</string>
<string name="key_spectrum_ext_post_gain" translatable="false">spectrum_ext_post_gain</string>
<string name="key_spectrum_ext_safety" translatable="false">spectrum_ext_safety</string>
<string name="key_spectrum_ext_hp_q" translatable="false">spectrum_ext_hp_q</string>
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,14 @@
<string name="spectrum_ext_enable">Spectrum extension</string>
<string name="spectrum_ext_strength">Strength</string>
<string name="spectrum_ext_strength_unit">Strength unit</string>
<string name="spectrum_ext_allow_boost">Allow strength above 0 dB</string>
<string name="spectrum_ext_allow_boost_summary">Experimental. Expands the strength range above stock limits.</string>
<string name="strength_unit_percent">Percent</string>
<string name="strength_unit_db">dB</string>
<string name="spectrum_ext_ref_freq">Reference frequency</string>
<string name="spectrum_ext_wet_mix">Wet mix</string>
<string name="spectrum_ext_wet_only_monitor">Monitor added signal only</string>
<string name="spectrum_ext_wet_only_monitor_summary">Mutes the original signal and outputs only Spectrum Extension content.</string>
<string name="spectrum_ext_post_gain">Post gain</string>
<string name="spectrum_ext_safety">Safety limiter</string>
<string name="spectrum_ext_safety_summary">Disabled by default. Enable to reduce overload and clipping.</string>
Expand Down
Loading
Loading