Add the contact list (friends, blocked, neutral)
Mirrors the TeamSpeak 3 client's Contacts: clients can be filed as friend,
blocked or neutral from their context menu or by dragging them into the
Contacts window, given a custom and phonetic nickname, shown under either
name or both, and per contact muted automatically, ignored in server/channel
or private chat and pokes, have their away message hidden and whispers
allowed or denied. Friends are green and blocked clients red in the tree
(optional), new contacts start from per-type defaults ("Set Defaults"), and
the global whisper policy lives in Options -> Contacts. "Change Volume..."
adds a per-client dB gain that a contact entry remembers.
Contacts are stored in ~/.ts3jclient/contacts.txt using the exact record
format the official client keeps in the Contacts table of its settings.db
(field names and codes recovered from the binary), so entries move between
the two verbatim. A small read-only pure-Java SQLite reader imports the
official client's list on first run and on demand, without a driver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,8 @@ public final class DesktopVoiceOutput implements VoiceOutput {
|
||||
|
||||
private final Map<Integer, ClientStream> streams = new ConcurrentHashMap<>();
|
||||
private final Set<Integer> mutedClients = ConcurrentHashMap.newKeySet();
|
||||
/** Per-client gain on top of the master volume; absent means 1.0. */
|
||||
private final Map<Integer, Double> clientVolumes = new ConcurrentHashMap<>();
|
||||
|
||||
private volatile String outputDevice;
|
||||
private volatile double masterVolume = 1.0;
|
||||
@@ -175,6 +177,15 @@ public final class DesktopVoiceOutput implements VoiceOutput {
|
||||
return mutedClients.contains(clientId);
|
||||
}
|
||||
|
||||
public void setClientVolume(int clientId, double gain) {
|
||||
if (gain == 1.0) clientVolumes.remove(clientId);
|
||||
else clientVolumes.put(clientId, Math.max(0, gain));
|
||||
}
|
||||
|
||||
public double getClientVolume(int clientId) {
|
||||
return clientVolumes.getOrDefault(clientId, 1.0);
|
||||
}
|
||||
|
||||
public void setOutputDevice(String device) {
|
||||
this.outputDevice = device;
|
||||
}
|
||||
@@ -333,7 +344,7 @@ public final class DesktopVoiceOutput implements VoiceOutput {
|
||||
try {
|
||||
float[] pcm = stream.pcm;
|
||||
int frames = stream.decoderFor(channels).decode(data, pcm);
|
||||
double vol = masterVolume;
|
||||
double vol = masterVolume * clientVolumes.getOrDefault(stream.clientId, 1.0);
|
||||
|
||||
// Match the decoded stream to the line: duplicate mono across a stereo
|
||||
// line, fold a stereo (music) stream down onto a mono-only line.
|
||||
@@ -375,9 +386,12 @@ public final class DesktopVoiceOutput implements VoiceOutput {
|
||||
}
|
||||
|
||||
/** Drop a speaker's pipeline entirely (e.g. they left the server). */
|
||||
/** Forgets a client that left; the server may hand its id to somebody else. */
|
||||
public void removeClient(int clientId) {
|
||||
ClientStream s = streams.remove(clientId);
|
||||
if (s != null) s.close();
|
||||
mutedClients.remove(clientId);
|
||||
clientVolumes.remove(clientId);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
||||
Reference in New Issue
Block a user