# Musik Spektrum Anzeige erzeugen



## thomas.g (10. Mai 2005)

Hi,

weiß vielleicht wer, wie man zum Beispiel während abspielen einer wav Datei eine Spektrum Anzeige erzeugt werden kann? (das ist das mit den "hüpfenden" Balken, siehe Windows Media Player).

Ich bräuchte das für eine Musikplayer.

thx


----------



## 0xdeadbeef (10. Mai 2005)

Ich bin mir zwar nicht ganz sicher, was Du mit einem springenden Ball meinst, aber die ganzen Mediaplayer-Visualisierungen und dergleichen beruhen auf einer FFT (Fast Fourier Transformation). Einfach gesprochen wird ein Signal dabei in die enthaltende harmonischen (sinusförmigen) Frequenzanteile zerlegt.

Das ganze Gebiet (Echtzeiterfassung der abgespielten Daten über DirectSound-Interface o.ä., gefensterte FFT alle x Millisekunden, Visualisierung) halte ich für denkbar ungeeignet für Java. In jedem Fall braucht man JNI, um überhaupt an die abgespielten Daten zu kommen. Die FFT könnte von der Performance her auch problematisch werden, wobei man wohl damit leben könnte. Schätze aber mal, daß das in "echten" Playern mit irgendwelchen MMX/SIMD-Befehlen massiv beschleunigt ist und man mit Java dramatisch langsamer ist.


----------



## Roar (10. Mai 2005)

0xdeadbeef hat gesagt.:
			
		

> Ich bin mir zwar nicht ganz sicher, was Du mit einem springenden Ball meinst



er sagte "balken" nicht ball 

ich glaub der meint nicht die visualisierungen sondenr das spektrum (das frequenzanzeigeding oder so)


----------



## 0xdeadbeef (10. Mai 2005)

Hm, falls da schon immer "Balken" stand, habe ich wohl gepennt   

Wie auch immer: wenn in Frequenzen zerlegt werden soll, bietet sich eine FFT an.
Die Visualisierungen sind letztendlich nur "aufgebohrte" Peak Level Meter für verschiedene Frequenzbereiche.


----------



## bazz-dee (11. Mai 2005)

Oxdeadbeaf hat Recht, das funktioniert über Fouriertransformation.


Also ich versuche das mal kurz zu fassen.

du hast eine nicht sinusförmige Schwingung und musst diese in nicht sinusförmige teile zerlegen.

Dann wird das ergebnis:

Y(t) = (A0 / 2)  + Summe von n=1 bis unendlich über [An * cos(n*W0*t) + Bn * sin(n*W0*t)]

mit A0 = (2/T) * Integral über einer Periode [y(t) dt]
An = (2/T) * Integral über eoner Periode [y(t) * cos(n*W0*t)dt]
Bn = (2/T) * Integral über eoner Periode [y(t) * sin(n*W0*t)dt]

Man kann aber bei einigen Funktionen vorher shcon sagen das zB An oder Bn = 0 ist.
Wenn die Funktion zB Achsensymmetrisch zur Y-Achse ist dass ist die Funktion nur cos-Abhängig und Bn fällt weg, ist die Funktion Punktsymmetrisch zum Ursprung so ist An = 0 da die Funktion nur von sin abhängt.


Hoffe ich hab kein Fehler gemacht jetz.
Also schriftlich rechnen kann ich das, ich denke es ist etwas schwieriger das ganze zu implementieren.


----------



## thomas.g (7. Jun 2005)

so, jetzt stellt sich also die Frage, wie bekommen ich mit JMF die Frequenzbereiche heraus, damit eine Visualisierung möglich ist?


----------



## lin (25. Jul 2005)

hej, ich habe auch Interesse an der FFT. Nun Wollte ich dich mal fragen, ob du das geschafft hast.

Ich habe eine Methode in dem JavaForum von Sun gefunden (leider ohne Copyright Bestimmungen und dergleichen...)
Leider funktioniert diese bei mir nicht wirklich, vielleicht fehlen auch Teile vom Code


```
public class FFT {
	/**
	 * This is a Java implementation of the fast Fourier transform
	 * written by Jef Poskanzer. The copyright appears above.
	 */
 
	private static final double TWOPI = 2.0 * Math.PI;
	
	// Limits on the number of bits this algorithm can utilize
	private static final int LOG2_MAXFFTSIZE = 15;
	private static final int MAXFFTSIZE = 1 << LOG2_MAXFFTSIZE;
 
	/**
	 * FFT class constructor
	 * Initializes code for doing a fast Fourier transform
	 *
	 * @param int bits is a power of two such that 2^b is the number
	 * of samples.
	 */
	public FFT(int bits) {
 
		this.bits = bits;
 
		if (bits > LOG2_MAXFFTSIZE) {
			System.out.println("" + bits + " is too big");
			System.exit(1);
		}
		for (int i = (1 << bits) - 1; i >= 0; --i) {
			int k = 0;
			for (int j = 0; j < bits; ++j) {
				k *= 2;
				if ((i & (1 << j)) != 0)
					k++;
			}
			bitreverse = k; //<--- hier meckert eclipse >> Prob. A
		}
	}
 
	/**
	 * A fast Fourier transform routine
	 *
	 * @param double [] xr is the real part of the data to be transformed
	 * @param double [] xi is the imaginary part of the data to be transformed
	 * (normally zero unless inverse transofrm is effect).
	 * @param boolean invFlag which is true if inverse transform is being
	 * applied. false for a forward transform.
	 */
	public void doFFT(double [] xr, double [] xi, boolean invFlag) {
		int n, n2, i, k, kn2, l, p;
		double ang, s, c, tr, ti;
 
		n2 = (n = (1 << bits)) / 2;
 
		for (l = 0; l < bits; ++l) {
			for (k = 0; k < n; k += n2) {
				for (i = 0; i < n2; ++i, ++k) {
					p = bitreverse[k / n2];
					ang = TWOPI * p / n;
					c = Math.cos(ang);
					s = Math.sin(ang);
					kn2 = k + n2;
					
					if (invFlag)
						s = -s;
 
					tr = xr[kn2] * c + xi[kn2] * s;
					ti = xi[kn2] * c - xr[kn2] * s;
 
					xr[kn2] = xr[k] - tr;
					xi[kn2] = xi[k] - ti;
					xr[k] += tr;
					xi[k] += ti;
				}
			}
			n2 /= 2;
		}
 
		for (k = 0; k < n; k++) {
			if ((i = bitreverse[k]) <= k)
				continue;
 
			tr = xr[k];
			ti = xi[k];
			xr[k] = xr; //<-- hier meckert eclipse >>>
			xi[k] = xi; //<-- hier meckert eclipse >>>  Prob. B
			xr = tr; //<-- hier meckert eclipse     >>>
			xi = ti; //<-- hier meckert eclipse      >>>
		}
 
		// Finally, multiply each value by 1/n, if this is the forward
		// transform.
		if (!invFlag) {
			double f = 1.0 / n;
 
			for (i = 0; i < n ; i++) {
				xr *= f; //<-- hier meckert eclipse >>>
				xi *= f; //<-- hier meckert eclipse >>> Prob. C
			}
		}
	}
	// Private class data
	private int bits;
	private int [] bitreverse = new int[MAXFFTSIZE];
}
```

Wäre dankbar, wenn mich jemand darüber aufklären könnte, weshalb eclipse folgende Fehler erkennt, bzw. was an dem Code noch nicht stimmt, oder was fehlt. 
Prob. A: int k - FFT.FFT(int) <<<nur das schlägt vor, entweder bitreverse zu von int[] zu int zu wechseln oder k von int zu int[]
Prob. B: cannot convert from double[] to double (eigentl. recht ähnlich zu Problem A)
Prob. C: The operator *= is undefined for the argument type(s) double[], double 

Oder vielleicht hat mir jemand (möglichst verständlichen) Code von ner andren Klasse die die FFT auf ein Array von Werten anwenden kann, weil ich habe definitiv nicht das Wissen um mir selber so ne Klasse zu coden :cry:

Danke für Antworten


----------



## thomas.g (25. Jul 2005)

naja, ich habe mein Problem nur indirekt lösen können.
Ich weiß zwar, wie man mit JMF die ganzen Frequenzen von einer Datei auslesen kann doch jetzt müsste das zeichnen des Spectrums Synchron mit der Wiedergabe laufen was allerdings schwer bis gar nicht zu realisieren ist, da JMF keine Funktion bietet, die feststellen kann, welche Frequenz gerade gespielt wird.
Außerdem weiß ich noch immer nicht ganz warum ich für das zeichnen eines Spectrums FFT brauche. Wenn micht jemand darüber aufklären könnte wäre ich sehr dankbar!
thx, thomas


----------



## lin (25. Jul 2005)

Leider verstehe ich die FFT auch net so ganz, aber mit Hilfe von ihr kann man das Audiosignal in verschiedene Frequenzen aufspalten. 
Dann kann man Balken zeichnen die die gegenwärtige Amplitude in einem bestimmten Frequenzbereich darstellen. Wenn zum Bsp. dein Player "boo" spielt (), dann kannst du mit der FFT von diesem Ton ein Frequenzspektrum erstellen und dann die Intensität der Frequenzbereiche von z.B. 20Hz - 200Hz, 200Hz - 400 Hz, 400Hz-1000 Hz, mit Balken repräsentieren. 

Ich beabsichtige die FFT auf ein mit einer DigiCam aufgenommenes Bild anzuwenden, um so auf die tatsächliche Auflösung der Optik und des CCDs schliessen zu können.
Nur leider finde ich keine Klasse um die FFT durchführen zu können  ... bzw. keine Klasse die läuft....


----------



## thomas.g (26. Jul 2005)

ok, ich hab mir das mal angeschaut und den Code copiert, aber, was muss ich jetzt genau bei dieser doFFT Methode für Parameter angeben damit ich mein Signal in Frequenzen spalten kann?
PS: Bis jetzt mekert NetBeans nicht und die Operatoren bei deinem Problem (*=) habe ich mit x = x * y ausgestaust.
thx, thomas


----------



## lin (26. Jul 2005)

Hm, komisch, Eclipse meckert..... naja. 

In diesem Forum steht geschrieben, das die doFFT Methode die Daten in den Arrays die du übergibst verändert..


```
// Declare your arrays
double[] realData;
double[] imaginaryData;
 
/*
Init and fill your data from where you want...
*/
 
// Obtain an FFT instance
FFT fft = new FFT(/* int bits*/);
 
// Call the method
fft.doFFT(realData, imaginaryData, true);
 
/*
Do whatever you need with your array, knowing that their content has been modified...
*/
```

Und dann bekommst du halt die Frequenzanteile zurück.. Musst diese dann in verschiedene Balken unterteilen und darstellen (irgendwie  ) Nur kann ich mir kaum vorstellen, dass dieser Algorithmus hier schnell genug ist. Wenn man bedenkt, dass er das ganze jede Sekunde ein paar mal duchlaufen muss...

Ich habe noch einen Code gefunden (von ner japanischen Seite, deshalb die komischen, und sehr spärlichen Kommentare  )
Dieser Code wird von Eclipse (so wie's bei mir konfiguriert ist, akzeptiert 

```
public class FFT {
    int n;
    int[] bitrev;
    double[] sintbl;

    public FFT(int n) {
        this.n = n;
        sintbl = new double[n + n / 4];
        bitrev = new int[n];
        // ŽOŠpŠÖ?”•\‚ð?ì‚é.
        double t = Math.sin(Math.PI / n);
        double dc = 2 * t * t;
        double ds = Math.sqrt(dc * (2 - dc));
        t = 2 * dc;
        double c = sintbl[n / 4] = 1;
        double s = sintbl[0] = 0;
        for (int i = 1; i < n / 8; i++) {
            c -= dc;  dc += t * c;
            s += ds;  ds -= t * s;
            sintbl[i] = s;  sintbl[n / 4 - i] = c;
        }
        if (n / 8 != 0) sintbl[n / 8] = Math.sqrt(0.5);
        for (int i = 0; i < n / 4; i++)
            sintbl[n / 2 - i] = sintbl[i];
        for (int i = 0; i < n / 2 + n / 4; i++)
            sintbl[i + n / 2] = - sintbl[i];
        // ƒrƒbƒg”½“]•\‚ð?ì‚é.
        int i = 0;
        int j = 0;
        bitrev[0] = 0;
        while (++i < n) {
            int k = n / 2;
            while (k <= j) {  j -= k;  k /= 2;  }
            j += k;
            bitrev[i] = j;
        }
    }

    void fftsub(double[] x, double[] y, int sign) {
        for (int i = 0; i < n; i++) { // ƒrƒbƒg”½“]
            int j = bitrev[i];
            if (i < j) {
                double t = x[i];  x[i] = x[j];  x[j] = t;
                t = y[i];  y[i] = y[j];  y[j] = t;
            }
        }
        for (int k = 1; k < n; k *= 2) { // •ÏŠ·
            int h = 0;
            int d = n / (k * 2);
            for (int j = 0; j < k; j++) {
                double c = sintbl[h + n / 4];
                double s = sign * sintbl[h];
                for (int i = j; i < n; i += k * 2) {
                    int ik = i + k;
                    double dx = s * y[ik] + c * x[ik];
                    double dy = c * y[ik] - s * x[ik];
                    x[ik] = x[i] - dx;  x[i] += dx;
                    y[ik] = y[i] - dy;  y[i] += dy;
                }
                h += d;
            }
        }
    }

    public void fft(double[] x, double[] y) {
        fftsub(x, y, 1);
    }

    public void ifft(double[] x, double[] y) {
        fftsub(x, y, -1);
        for (int i = 0; i < n; i++) {
            x[i] /= n;  y[i] /= n;
        }
    }

    // ƒeƒXƒg—p
    public static void main(String[] args) {
        final int N = 64;
        double x1[] = new double[N];
        double y1[] = new double[N];
        double x2[] = new double[N];
        double y2[] = new double[N];
        double x3[] = new double[N];
        double y3[] = new double[N];
        for (int i = 0; i < N; i++) {
            x1[i] = x2[i] = 6 * Math.cos( 6 * Math.PI * i / N)
                          + 4 * Math.sin(18 * Math.PI * i / N);
            y1[i] = y2[i] = 0;
        }
        FFT fftN = new FFT(N);
        fftN.fft(x2, y2);
        for (int i = 0; i < N; i++) {
            x3[i] = x2[i];  y3[i] = y2[i];
        }
        fftN.ifft(x3, y3);
        System.out.println("(Œ³ƒf?[ƒ^) (ƒt?[ƒŠƒG•ÏŠ·) (‹t•ÏŠ·)");
        for (int i = 0; i < N; i++)
            System.out.println(i + " (" +
                               (float)x1[i] + "," +
                               (float)y1[i] + ") (" +
                               (float)x2[i] + ", " +
                               (float)y2[i] + ") (" +
                               (float)x3[i] + ", " +
                               (float)y3[i] + ")");
    }
}
```

Ich bin auch noch auf den Code hier gestossen, in der Mitte der Page hat es eine class Fft. Die macht einen sehr kompakten Eindruck  .


----------



## Exceptionist (22. Okt 2005)

mein gewünschtes anwendungsgebiet wäre ein equalizer, damit man je nach wunsch diverse frequenzen einfach "killen" kann.

ich mixe ab und an musik und ein kollege hat mich gefragt, ob man einen equalizer mit java realisieren kann.
aus euren bisherigen beiträgen hier bekomme ich positive, wie auch negtive informationen für meine anwendungs-idee.

einerseits weiß ich jetzt, dass man die frequenzen einer audio-file splitten und auswerten kann, andererseits läuft das ganze dann wahrscheinlich nicht in echtzeit ab... oder?? (wie gross wäre eine eventuelle verzögerung?)

würd mich freuen, wenn ihr was dazu sagen könntet, ich benötige noch keine codes oder code-fragmente, ich muss die ganze sache erst konkret durchdenken und mir nen "plan" machen, wie ich die ganze action starte.


----------



## thomas.g (23. Okt 2005)

das ganze lässt sich wunderbar mit JavaSound realisieren, allerdings beschränkt sich das ganze dann auf .wav und .mp3 dateien, alles andere geht nicht, naja außer .aif und .au die gehen auch noch. 

Ich suchen schon lange nach einer Lösung, javasound mit JMF zu verknüpfen.

Einerseits brauche ich nämlich für meinen Player die Kontroller von JMF und andereseits würde ich die Kontroller von javasound brauchen.

Hätte wer eine Lösung, wie ich zum Beispiel einen AudioInputStream in JMF nutzen kann um die Wiedergabe zu starten?

thx, thomas


----------



## lin (23. Okt 2005)

@Exceptionist
Mit einer Fast Fourier Transformation (FFT) ist die Verzögerung klein. Mal kleine Schätzung: Also um ein Spektrum von 50Hz - 20'000Hz zu zeichnen, musst du ein Sample von ~20ms nehmen, die Analyse davon dauert dann so ca. 1ms (~2048Werte), das ergibt ne Verzögerung von 1/50s. Du kannst natürlich auch zuerst die Analyse machen, und dann abspielen.

Aber für Equalizer kannste auch winamp nehmen ;-)


----------



## Exceptionist (24. Okt 2005)

lin hat gesagt.:
			
		

> Aber für Equalizer kannste auch winamp nehmen ;-)



wenn ich Winamp(diese durak-software :noe: ) nehmen wollte, würde ich mich nicht an einen eigenen equalizer denken...

ich brauche halt nen ganzen Batzen Frequenzen, die ich killen kann... es geht darum zB auch lästigen gesang aus wertvoller musik zu verbannen...
meine kollegen und ich sind ein wenig verrückt... zumindest wenn es um musik und klangbreite geht.
our core 4 hard life


----------



## lin (24. Okt 2005)

> es geht darum zB auch lästigen gesang aus wertvoller musik zu verbannen...


huch, klingt kompliziert, weil die Frequenz des Gesangs variiert ja ständig? *interessier*


----------



## thomas.g (24. Okt 2005)

eigentlich könnte man mit JMF einen Euqalizer machen, man müsste halt seinen eigenen Controller schreiben, ich arbeite gerade an einem, der die derzeitige lautstärke (peak) zurückgibt, auf diese Art und Weise kann ich dann zum Beispiel so ein AnalyseTool machen, das während der Wiedergabe immer den Peak und die Frequenz angibt. (nettes Spielzeug für meinen Player ^^)

ich sag dann bescheid obs gefunzt hat, das mit dem Peak funktioniert schon mal so einigermaßen aber sonst .....


----------



## Exceptionist (25. Okt 2005)

@ bratan LIN

es wird ne ganze weile dauern, bis ich die anwendung geschrieben habe(bin ja noch in der überlegungsphase)... sobald ich es aber vollbracht habe, sag ich dir bescheid.
auf besonderen wunsch würde ich auch den quellcode abgeben, damit du dich an ihm vergehen kannst!

und zu den variierenden frequenzen: ich hatte die idee, ein paar slider zu machen, bei denen man eine frequenz angeben kann... damit hat man dann die möglichkeit einen eigenen frequenzbereich einzugrenzen.
also vom theoretischen ansatz ( den ich mit einigen kollegen schon besprochen habe) sollte die ganze sache funktionieren.


----------

