Treble bleed enhancements

This commit is contained in:
Bane Stojkovic
2026-06-07 22:37:27 +02:00
parent f481542d5a
commit efa803d7d0
5 changed files with 150 additions and 31 deletions
@@ -118,4 +118,10 @@ public class Resistance extends AbstractMeasure<ResistanceUnit> {
}
throw new IllegalArgumentException("Could not parse resistance: " + value);
}
public Double getNormalizedValue() {
if (value == null || unit == null)
return null;
return value * unit.getFactor();
}
}
@@ -234,7 +234,7 @@ public class Tree {
public Set<IDIYComponent<?>> extractComponents(Set<String> types) {
Set<IDIYComponent<?>> res = new HashSet<IDIYComponent<?>>();
if (leaf != null && types.contains(leaf.getComponent().getClass().getCanonicalName())) {
if (leaf != null && (types == null || types.contains(leaf.getComponent().getClass().getCanonicalName()))) {
res.add(leaf.getComponent());
} else if (children != null) {
for (Tree t : children) {
@@ -389,6 +389,14 @@
<changeType>NEW_FEATURE</changeType>
<description>IEC variations of fuse and potentiometer symbols</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Detect the style of a treble bleed based on the topology and component values (e.g. Fender, DiMarzio, Kinman, etc)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Tone capacitor mistaken for treble bleed when connected to the input lug of the volume pot</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Dragging a component from the component tree to the canvas sometimes does not work as expected without clicking on it first and then dragging it</description>
@@ -18,14 +18,7 @@
*/
package org.diylc.netlist;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import javax.swing.JLabel;
@@ -59,6 +52,7 @@ public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements IN
private static Set<String> CAP_TYPES = new HashSet<String>();
private static Set<String> RESISTOR_TYPES = new HashSet<String>();
private static Set<String> RC_TYPES = new HashSet<String>();
private static Set<String> ALL_TYPES = null;
static {
JACK_TYPES.add(OpenJack1_4.class.getCanonicalName());
@@ -273,15 +267,15 @@ public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements IN
// locate volume and tone pots
Map<IDIYComponent<?>, List<IDIYComponent<?>>> volPotPickupMap =
new HashMap<IDIYComponent<?>, List<IDIYComponent<?>>>();
Map<IDIYComponent<?>, Set<IDIYComponent<?>>> volPotTrebleBleedMap =
new HashMap<IDIYComponent<?>, Set<IDIYComponent<?>>>();
new HashMap<>();
Map<IDIYComponent<?>, TrebleBleed> volPotTrebleBleedMap =
new HashMap<>();
Map<IDIYComponent<?>, List<IDIYComponent<?>>> tonePotPickupMap =
new HashMap<IDIYComponent<?>, List<IDIYComponent<?>>>();
new HashMap<>();
Map<IDIYComponent<?>, List<IDIYComponent<?>>> tonePotPickupReverseMap =
new HashMap<IDIYComponent<?>, List<IDIYComponent<?>>>();
new HashMap<>();
Map<IDIYComponent<?>, List<IDIYComponent<?>>> volPotPickupReverseMap =
new HashMap<IDIYComponent<?>, List<IDIYComponent<?>>>();
new HashMap<>();
Set<IDIYComponent<?>> pots = tree.extractComponents(POT_TYPES);
for (IDIYComponent<?> pot : pots) {
@@ -360,12 +354,12 @@ public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements IN
}
private static void outputVolumePotentiometer(Map.Entry<IDIYComponent<?>, List<IDIYComponent<?>>> e,
List<String> notes, Map<IDIYComponent<?>, Set<IDIYComponent<?>>> volPotTrebleBleedMap) {
List<String> notes, Map<IDIYComponent<?>, TrebleBleed> volPotTrebleBleedMap) {
StringBuilder sb = new StringBuilder(
"'" + e.getKey().getName() + "' potentiometer acts as a volume control for ");
boolean first = true;
List<String> pickupNames =
e.getValue().stream().map(x -> x.getName()).sorted().collect(Collectors.toList());
e.getValue().stream().map(IDIYComponent::getName).sorted().toList();
for (String pickupName : pickupNames) {
if (!first)
sb.append(pickupNames.size() == 2 ? " and " : ", ");
@@ -374,21 +368,31 @@ public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements IN
}
notes.add(sb.toString());
Set<IDIYComponent<?>> trebleBleedComponents = volPotTrebleBleedMap.get(e.getKey());
if (trebleBleedComponents != null && trebleBleedComponents.size() > 0) {
TrebleBleed trebleBleed = volPotTrebleBleedMap.get(e.getKey());
Set<IDIYComponent<?>> trebleBleedComponents = trebleBleed.components();
if (trebleBleedComponents != null && !trebleBleedComponents.isEmpty()) {
String trebleBleadNote = trebleBleedComponents.stream()
.sorted(Comparator.comparing(c -> c.getName()))
.map(c -> "'" + c.getName() + "'")
.collect(Collectors.joining(", ")) + " form a treble bleed network on the '" + e.getKey().getName() + "' volume control";
notes.add(trebleBleadNote);
TrebleBleed.AnalysisResult analyze = trebleBleed.analyze();
String strComponents =
trebleBleedComponents.stream().sorted(Comparator.comparing(IDIYComponent::getName))
.map(c -> "'" + c.getName() + "'").collect(Collectors.joining(", "));
String trebleBleedNote = strComponents + (trebleBleedComponents.size() == 1 ? " forms a " : " form a ");
if (analyze.circuitType() != null) {
trebleBleedNote += analyze.circuitType() + " ";
}
trebleBleedNote = trebleBleedNote + "treble bleed circuit on the '" + e.getKey().getName() + "' volume control";
if (analyze.style() != null) {
trebleBleedNote = trebleBleedNote + ", in the style of " + analyze.style();
}
notes.add(trebleBleedNote);
}
}
private void processPotentiometer(IDIYComponent<?> pot, Tree tree, Map<IDIYComponent<?>, Tree> pickupRoots,
Map<IDIYComponent<?>, List<IDIYComponent<?>>> tonePotPickupReverseMap,
Map<IDIYComponent<?>, List<IDIYComponent<?>>> tonePotPickupMap,
Map<IDIYComponent<?>, Set<IDIYComponent<?>>> volPotTrebleBleedMap,
Map<IDIYComponent<?>, TrebleBleed> volPotTrebleBleedMap,
Map<IDIYComponent<?>, List<IDIYComponent<?>>> volPotPickupMap,
Map<IDIYComponent<?>, List<IDIYComponent<?>>> volPotPickupReverseMap) {
TreeLeaf leaf1 = new TreeLeaf(pot, 0, 1);
@@ -459,17 +463,27 @@ public class GuitarDiagramAnalyzer extends AbstractNetlistAnalyzer implements IN
}
private void locateTrebleBleed(Tree tree,
Map<IDIYComponent<?>, Set<IDIYComponent<?>>> volPotTrebleBleedMap, IDIYComponent<?> pot,
Map<IDIYComponent<?>, TrebleBleed> volPotTrebleBleedMap, IDIYComponent<?> pot,
Tree tree1) {
Tree parent1 = tree1;
do {
parent1 = tree.findParent(parent1);
} while (parent1 != null && parent1.getChildren().size() <= 1);
if (parent1 != null) {
Set<IDIYComponent<?>> trebleBleedComponents = parent1.extractComponents(RC_TYPES);
if (trebleBleedComponents.stream().anyMatch(c -> CAP_TYPES.contains(c.getClass().getCanonicalName()))) {
volPotTrebleBleedMap.computeIfAbsent(pot, k -> new HashSet<IDIYComponent<?>>()).addAll(trebleBleedComponents);
}
Optional<Tree> trebleTree =
parent1.getChildren().stream().filter(t -> !t.equals(tree1)).findFirst();
trebleTree.ifPresent(t -> {
// transcend one layer down if the tree is nested
while (t.getChildren().size() == 1 && t.getChildren().get(0).getLeaf() == null) {
t = t.getChildren().get(0);
}
Set<IDIYComponent<?>> trebleBleedComponents = t.extractComponents(ALL_TYPES);
if (trebleBleedComponents.stream().allMatch(c -> RC_TYPES.contains(c.getClass().getCanonicalName())) &&
trebleBleedComponents.stream().anyMatch(c -> CAP_TYPES.contains(c.getClass().getCanonicalName()))) {
TrebleBleed trebleBleed = new TrebleBleed(trebleBleedComponents, t.getConnectionType());
volPotTrebleBleedMap.put(pot, trebleBleed);
}
});
}
}
@@ -0,0 +1,91 @@
package org.diylc.netlist;
import org.diylc.core.IDIYComponent;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public record TrebleBleed(Set<IDIYComponent<?>> components, TreeConnectionType connectionType) {
public AnalysisResult analyze() {
List<Double> capValues = new ArrayList<>();
List<Double> resValues = new ArrayList<>();
for (IDIYComponent<?> c : components) {
if (c.getValue() instanceof org.diylc.core.measures.Capacitance) {
Double nv = ((org.diylc.core.measures.Capacitance) c.getValue()).getNormalizedValue();
if (nv != null) capValues.add(nv);
} else if (c.getValue() instanceof org.diylc.core.measures.Resistance) {
Double nv = ((org.diylc.core.measures.Resistance) c.getValue()).getNormalizedValue();
if (nv != null) resValues.add(nv);
}
}
String circuitType;
String style;
if (components.size() == 1) {
circuitType = null;
style = "PRS, G&L";
if (capValues.size() == 1) {
Double cVal = capValues.get(0);
if (matches(cVal, 180e-6)) {
style = "PRS";
} else if (matches(cVal, 200e-6)) {
style = "G&L";
}
}
} else if (components.size() == 2 && connectionType == TreeConnectionType.Series) {
circuitType = "series";
style = "Kinman";
if (capValues.size() == 1 && resValues.size() == 1) {
Double cVal = capValues.get(0);
Double rVal = resValues.get(0);
if (matches(cVal, 1.2e-3) && matches(rVal, 130e3)) {
style = "Kinman";
}
}
} else if (components.size() == 2 && connectionType == TreeConnectionType.Parallel) {
circuitType = "parallel";
style = "Fender, Suhr, Fralin, Bill Lawrence, Mojo Tone, TV Jones, DiMarzio, Seymour Duncan";
if (capValues.size() == 1 && resValues.size() == 1) {
Double cVal = capValues.get(0);
Double rVal = resValues.get(0);
if (matches(cVal, 330e-6) && matches(rVal, 80e3)) {
style = "Bill Lawrence";
} else if (matches(cVal, 471e-6) && matches(rVal, 220e3)) {
style = "Mojo Tone";
} else if (matches(cVal, 560e-6) && matches(rVal, 300e3)) {
style = "DiMarzio";
} else if (matches(cVal, 680e-6) && matches(rVal, 150e3)) {
style = "John Suhr";
} else if ((matches(cVal, 1e-3) || matches(cVal, 2e-3)) && matches(rVal, 150e3)) {
style = "TV Jones";
} else if (matches(cVal, 1e-3) && matches(rVal, 100e3)) {
style = "Seymour Duncan";
} else if (matches(cVal, 2.5e-3) && matches(rVal, 200e3)) {
style = "Lindy Fralin";
}
}
} else if (components.size() == 3) {
circuitType = null;
style = null;
if (capValues.size() == 1 && resValues.size() == 2) {
Double cVal = capValues.get(0);
if (matches(cVal, 1.2e-3) && ((matches(resValues.get(0), 150e3) && matches(resValues.get(1), 20e3)) || (matches(resValues.get(0), 20e3) && matches(resValues.get(1), 150e3)))) {
style = "Fender";
}
}
} else {
circuitType = null;
style = null;
}
return new AnalysisResult(circuitType, style);
}
private static boolean matches(Double val, double expected) {
return val != null && Math.abs(val - expected) < expected * 1e-4;
}
public record AnalysisResult(String circuitType, String style) {}
}