Mehrere sound byte arrays mixen um sie über eine SourceDataLine auszugeben

Was auf der verlinkten Seite steht hat schon Hand und Fuß, aber leider schweigt sich der Autor darüber aus inwiefern seine Lösung (für mehrere Kanäle) vernünftig klingt. -_-

Die Umsetzung seiner Formel in Java kann eigentlich ersteinmal ohne Umschweife geschehen:

    /**
     * 
     * @param sum
     *  Summe der PCM-Samples der einzelnen Kanäle, 
     *  normiert auf -1*channelCount bis +1*channelCount
     * 
     * @param channelCount
     *  Anzahl der Kanäle
     * 
     * @param alpha
     * 
     * @param threshold
     * 
     * @return 
     *  resultierendes, normiertes PCM-Sample (-1 bis +1)
     */
    private static double normalizeSum(
            double sum, 
            int channelCount, 
            double alpha, 
            double threshold)
    {
        double sign = sum >= 0 ? 1 : -1;
        double absolutValue = sum * sign;
        double result;
        
        if( absolutValue > threshold ) {
            double l1 = Math.log( 1d + alpha * (absolutValue-threshold) / (channelCount-threshold) );
            double l2 = Math.log( 1d + alpha );
            result = l1 / l2;
            result *= 1d - threshold;
            result += threshold;
            result *= sign;
        } else {
            result = sum;
        }
        
        return result;
    }

Zu beachten:

  • es wird davon ausgegegangen das die einzelnen Kanäle PCM-Werte zwischen -1.0 und +1.0 liefern; die Summe (sum) der PCM-Samples muss, bei channelCount vielen Kanälen, dementsprechend zwischen -channelCount und +channelCount liegen
  • den threshold kann man frei wählen, der Autor verwendet gerne den Wert 0.6
  • alpha wird gemäß dem threshold (und der Anzahl der Kanäle) errechnet. Auf der Webseite kann man sich leider nur die alphas bei 2 Kanälen berechnen lassen, wenn man mehr braucht muss man wohl seinen eigenen Kürbis verschleißen.

Bei konstantem threshold = 0.6 und alpha = 7.48 schaut das Resultat für 1-3 Kanäle so aus (türkis: Summe der PCM-Samples, blau: mit der obigen Methode kompandiertes Signal):

Man sieht glaube ich deutlich das die Parameter für 2 Kanäle hin optimiert wurden. Mit zunehmender Kanalzahl wird der Knick beim Thresholdwert 0,6 immer deutlicher. Da empfiehlt es sich wohl dringend bei mehr Kanälen zumindest den threshold herunterzusetzen, am besten aber auch das Alpha für die Anzahl der Kanäle entsprechend zu berechnen.

Das Ganze als Hörprobe (1.6 mb MP3 bei Share Online): Rock’n Roll Overdose/The Hawaiians
Sekunden 0-10: Original Musikschnipsel
Sekunden 10-20: als 1 Kanal gemixt
Sekunden 20-30: als 2 Kanäle gemixt
Sekunden 30-40: als 3 Kanäle gemixt
Sekunden 40-50: als 4 Kanäle gemixt
Sekunden 50-60: als 5 Kanäle gemixt
Sekunden 60-70: nochmal das Original
Für die Hörprobe wurde ein 10 Sekunden langer Musikschnipsel verwendet (PCM, Mono, 16 Bit, 44.100 Hz) und mit der obigen Methode in Java „gemixt“. Threshold war dabei konstant 0,6, alpha war konstant 7,48. Das Original Musikstück wurde dabei auf allen n Kanälen eingespeist - was sicherlich einen Extremfall darstellt. Die Güte der Ausgabe mag jeder selber bewerten. Ich persönlich denke aber das man mal eine Weile herumprobieren sollte um geeignete thresholds und alphas zu bestimmen.

PS:

Das ist nicht überraschend. Wenn die Summen den Wertebereich verlassen werden in deiner Implementierung die oberen Bits einfach abgeschnitten (Überlaufarithmethik), wodurch das Signal schon ziemlich gestört werden kann. Daher arbeitet man so einer Stelle gerne mit Sättigungsarithmetik.
Überlaufarithmetik: f(x) = x & 0xffff
Sättigungsarithmetik: f(x) = ( x>0xffff ? 0xffff : x ) bzw. ( x < 0 ? 0 : x )