Einen Sound mit veränderter Tonhöhe und Geschwindigkeit abspielen - Hilfe erbeten

Förster44

Mitglied
Hallo,
ich möchte in meinem aktuellen Programm die Funktionalität, Sounds mit veränderter Tonhöhe und Geschwindigkeit* abzuspielen anhand von Faktoren wie 0.4 oder 1.7 . Die Sounds habe ich im .wav-Format (16bit) vorliegen. Da ich nun aber seit 2 Tagen rumprobiere, teste und versuche und die Ergebnisse recht kläglich bleiben, möchte ich hier Hilfe/Unterstützung erbitten (und würde mich natürlich sehr freuen wenn ich sie auch bekomme ;) )
Hier nun also meine bisherigen Versuche:
Zunächst die Konvertierung: Da ich ja 2 byte pro Frame "geliefert" bekam, hab ich diese mit folgendem in ein Int-Array (das dann bearbeitet wurde) gepackt und später wieder zurückkonvertiert:
Java:
        static int[] toUnsignedByte(byte[] b)
	{
		int[] ret = new int[b.length/2];
		for(int i=0; i<ret.length; i++)
		{
			ret[i] = (int)(( ((int)b[i*2]) << 8) | ((int)b[i*2+1]));
		}
		return ret;
	}
	
	static byte[] toByte(int[] i)
	{
		byte[] ret = new byte[i.length*2];
		for(int j=0; j<i.length; j++)
		{
			ret[j*2] = (byte) (i[j] >> 8);
			ret[j*2+1] = (byte) (i[j] & 255);
		}
		return ret;
	}
Hier mein 1. Algorithmus (als erster Anlauf ein meinem Empfinden nach eher grobes Gehacke, das aber interessanterweise noch eher in manchen Fällen nach dem Original klingt als mein zweiter Versuch):
Java:
	static byte[] pitch(int[] toPlay/*das "Originalarray" wurde bereits in konvertierter Form geliefert*/, float var)
	{
                float stretchFact = (1/var);
		
		int[] neu = new int[Math.round(toPlay.length*(1/var))];
		for(int i=0; i<neu.length; i++)
		{
			neu[i] = toPlay[(int)(i/stretchFact)];
		}
		
		int[] geglättet = new int[neu.length];
		geglättet[0] = neu[0];
		for( int i=1; i<neu.length-1; i++)
		{
			if(i % stretchFact == 0)
			{
				geglättet[i] = neu[i];
				continue;
			}
			geglättet[i] = (int)((neu[i-1]*2+neu[i]+neu[i+1]*2) / 5.0 + 0.5);
		}
		geglättet[neu.length-1] = neu[neu.length-1];
		
		for(int i = 0; i < geglättet.length; i++)
			if(Math.round(stretchFact*i) < geglättet.length)  
			  geglättet[Math.round(stretchFact*i)] = toPlay[i];
		return toByte(geglättet);
	}

Hier mein zweiter Versuch, diesmal unter dem Leitmotiv, den "Streckfaktor" zu verzehnfachen, die Lücken mit "linearen Übergängen" zu füllen ({60, 0, 0, 30} -> {60, 50, 40, 30}) und dann aus diesem Array jeden zehnten Wert in das "finale" Array einzulesen.
Java:
        static byte[] pitch(int[] toPlay/*das "Originalarray" wurde bereits in konvertierter Form geliefert*/, float var)
	{
                int stretchFact = (int)((1/var)*10 + 0.5F);
		
		int[] temp = new int[toPlay.length*stretchFact];
		
		for(int i=0; i<toPlay.length; i++)
		{
			temp[i*stretchFact] = toPlay[i];
		}
		
		int step;
		for(int i=0; i<temp.length; i++)
		{
			step = i % stretchFact;
			if(step == 0)
				continue;
			try { temp[i] = ( toPlay[i/stretchFact] + toPlay[(i+stretchFact)/stretchFact] )*step/stretchFact; } catch(ArrayIndexOutOfBoundsException e) {}
		}
		
		int[] ret = new int[(int) (toPlay.length*(1/var)+0.5)];
		
		for(int i=0; i<ret.length; i++)
			try { ret[i] = temp[i*10]; } catch(ArrayIndexOutOfBoundsException e) {}
			
		return toByte(ret);

Wie gesagt, für Ratschläge/Hinweise/Tipps/Hilfe wäre ich sehr dankbar :)


*sowohl aus Gründen der "Einfachheit" als auch aus Designgründen
 

Noisefever

Mitglied
willst du tonhöhe und geschwindigkeit trennen? dann bist du auf dem falschen weg mit lücken linear füllen. willst du nur ganz normal die geschwindigkeit ändern (wodurch die tonhöhe natürlich entsprechend angepaßt wird), dann frage ich mich: warum der umstand? ist es wichtig daß du das rendern selber übernimmst?
 

Förster44

Mitglied
willst du tonhöhe und geschwindigkeit trennen?

Nein, ich möchte sie nicht trennen, sondern beide gemeinsam gleichermaßen ändern. Sorry falls ich mich da unklar ausgedrückt habe ;)

willst du nur ganz normal die geschwindigkeit ändern (wodurch die tonhöhe natürlich entsprechend angepaßt wird), dann frage ich mich: warum der umstand? ist es wichtig daß du das rendern selber übernimmst?

Umstand? Da gibts auch eine einfachere Variante die Geschwindigkeit (+Tonhöhe natürlich^^) zu ändern? Würde mich ja sehr freuen wenns so ist :)
 

Förster44

Mitglied
Entschuldigung für den Doppelpost, aber ich bin scheinbar malwieder zu blind um den Bearbeiten-Button zu finden :bahnhof:

Jedenfalls wollte ich nur erwähnen (weil ich eben bemerkt habe, dass ich das nicht grade gut erkennbar gemacht habe), dass das eigentliche Problem hier die Tatsache ist, dass beide meiner Tonhöhe- und-Geschwindigkeits-Veränderungs-Algorithmen nicht das erwünschte Ergebnis liefern und sich die Sounds im besten Falle zumindest ansatzweise nach dem Ursprunssound anhören (was aber Ausnahmefälle sind, im Normalfall werden meine Lautsprecherboxen von einem unerkennbaren rauschigen Kreischen verge******t) und ich da wirklich nicht mehr weiter weiß :(
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Da ich vor einigen Tagen auch mal was "in der Art" gemacht habe (also ein bißchen mit sounds rumgespielt... Frequenzanalyse mit FFT) : Was ist denn (in Worten) dein bisheriger Ansatz? (Ich würde ja den Code lesen, aber als ich ihn überflogen habe, sind mir bei [c]catch(ArrayIndexOutOfBoundsException e) {}[/c] die Augen geplatzt... )
 

Förster44

Mitglied
Okay, ich versuchs mal schriftlich weiter zu erläutern (ich werd dabei nicht allzu in die Details gehen^^)...

1. Codeabschnitt: Da das Audioformat in 16bit (->2 Bytes/Frame) vorliegt, muss ich ja erstmal, um die einzelnen Frames bearbeiten zu können, 2 Bytes zu jeweils einem Integer zusammenfügen (dann bearbeiten), später dann wieder zu 2 Bytes auseinanderfitzeln und dass dann ausgeben lassen.

2. Codeabschnitt: var ist der Faktor zum Ändern, wobei 0.5 das zu bearbeitende Array um den Faktor 2 streckt (und der Ton somit tiefer klingt) und 1.5 das Array um den Faktor 2/3 staucht (und der Ton somit höher klingt), weswegen ich den Streckfaktor 1/var berechne und dadurch auch die Größe des Outputarrays festlege. Dieses wird dann mit den zugehörigen Werten aus dem Ursprungsarray befüllt und später dann anhand des Durschnitts der jeweiligen Nachbarswerte etwas geglättet, wobei ich den Nachbarschaftswerten eine doppelte Wertigkeit gab, weil ... naja, ähm, Intuition :rtfm:
Ab da geht das Array auch schon direkt weiter an die Lautsprecher (oder zumindest an das, was dafür zuständig ist, dass es dort landet).

3. Codeabschnitt: Hier wollte ich ursprünglich etwas "sauberer" arbeiten *hust hat ja auch super geklappt hust* .... wieder mal mit dem Streckfaktor, nur wollte ich diesmal das Kommazeugs loswerden (ich hab dem irgendwie misstraut, vor allem wenn % zum Einsatz kam) und hab den Steckfaktor verzehnfacht, was ich damit ausgleichen wollte, dass ich aus dem dann entstehenden Array einfach nur jeden 10ten Wert wieder in das Outputarray einlese. Sobald also das "Zwischen"-Array anhand des größeren Streckfaktors erstellt ist, werden die Werte aus dem Ursprungsarray der Skalierung entsprechend darin eingefügt. In der folgenden Schleife werden "leere" Stellen im "Zwischen"-Array mit "linearen Überbrückungswerten" gefüllt, wobei bereits aus dem Ursprungsarray übernommene Werte unverändert bleiben (abstraktes Beispiel: {80, _, _, _, 40} wird zu {80, 70, 60, 50 , 40} (oder soll zumindest dazu werden)).
Im Anschluss wird dann noch, wie weiter oben "angekündigt", jeder zehnte Wert aus dem "Zwischen"-Array in das Output-Array übernommen und in die Weiten des restlichen Programms entlassen.

Ich hoffe das war jetzt verständlicher als mein Code^^
 

Marco13

Top Contributor
Uhrzeitbedingt hab' ich's jetzt nicht ganz nachvollzogen, aber ... ist das irgendwie Performancekritisch? Ich hatte bei meinen Experimenten dann irgendwann genug von Linker Kanal, Rechter Kanal, 1 oder 2 byte little oder big endian.... und hab' die Sounddaten dann einfach in einen FloatBuffer reingerechnet (der natürlich bei Bedarf auch wieder in eine abspielbare Form gebracht werden kann). Ganz naiv würde ich ja vermuten, dass man durch die Welle auch eine interpolierende Funktion durchlegen kann, und damit in bezug auf schnelleres oder langsameres Abspielen recht flexibel sein dürfte.
 
S

Spacerat

Gast
@Marco13: Das wollte ich auch grad' tippen. Liegst da gar nicht so verkehrt bzw. vollkommen richtig, zumindest, wenn er interpolieren will. Der FloatBuffer sollte dann aber nur Werte zwischen 0 und 1 speichern, nicht -1 bis +1, sonst wird das am Ende erfordeliche Biasing von Unsigned nach Singed ungenau.

Viel wichtiger aber ist, das er sich diese ganzen Array-Operationen (vergrössern, kopieren usw.) sparen kann, wenn er als Positionszeiger auf die Samples ein float verwendet, dann kann er mit der Formel:
Code:
(MAXFREQ / Periodendauer) / SAMPLERATE
diese Position inkremental verändern und die gecasteten ints * FRAMESIZE aus dieser Position zeigen immer auf das richtige Byte im original Array.
Die einzelnen Werte bedeuten:
MAXFREQ: maximale Frequenz der Tonhöhe.
Periodendauer: Dauer der Periode um eine gewisse Tonhöhe zu erreichen.
SAMPLERATE: Die Abtastrate des Widergabe-Audioformats.

Wie man auf die Werte MAXFREQ und Periodendauer kommt ist mir beim PC nicht geläufig. Ich hab' das Ganze aus 'ner Doku über ein uraltes Amiga Protracker2-Format. MAXFREQ ist dort 3546894,6Hz (Mittelwert zwischen PAL und NTSC) und die Periodenzeiten stehen in einer Tabelle.
Code:
		907, 900, 894, 887, 881, 875, 868, 862,
		856, 850, 844, 838, 832, 826, 820, 814, //C_1
		808, 802, 796, 791, 785, 779, 774, 768, //Db1
		762, 757, 752, 746, 741, 736, 730, 725, //D_1
		720, 715, 709, 704, 699, 694, 689, 684, //Eb1
		678, 674, 670, 665, 660, 655, 651, 646, //E_1
		640, 637, 632, 628, 623, 619, 614, 610, //F_1
		604, 601, 597, 592, 588, 584, 580, 575, //Gb1
		570, 567, 563, 559, 555, 551, 547, 543, //G_1
		538, 535, 532, 528, 524, 520, 516, 513, //Ab1
		508, 505, 502, 498, 495, 491, 487, 484, //A_1
		480, 477, 474, 470, 467, 463, 460, 457, //Bb1
		453, 450, 447, 444, 441, 437, 434, 431, //B_1
		428, 425, 422, 419, 416, 413, 410, 407, //C_2
		404, 401, 398, 395, 392, 390, 387, 384, //Db2
		381, 379, 376, 373, 370, 368, 365, 363, //D_2
		360, 357, 355, 352, 350, 347, 345, 342, //Eb2
		339, 337, 335, 332, 330, 328, 325, 323, //E_2
		320, 318, 316, 314, 312, 309, 307, 305, //F_2
		302, 300, 298, 296, 294, 292, 290, 288, //Gb2
		285, 284, 282, 280, 278, 276, 274, 272, //G_2
		269, 268, 266, 264, 262, 260, 258, 256, //Ab2
		254, 253, 251, 249, 247, 245, 244, 242, //A_2
		240, 239, 237, 235, 233, 232, 230, 228, //Bb2
		226, 225, 224, 222, 220, 219, 217, 216, //B_2
		214, 213, 211, 209, 208, 206, 205, 204, //C_3
		202, 201, 199, 198, 196, 195, 193, 192, //Db3
		190, 198, 188, 187, 185, 184, 183, 181, //D_3
		180, 179, 177, 176, 175, 174, 172, 171, //Eb3
		170, 169, 167, 166, 165, 164, 163, 161, //E_3
		160, 159, 158, 157, 156, 155, 154, 152, //F_3
		151, 150, 149, 148, 147, 146, 145, 144, //Gb3
		143, 142, 141, 140, 139, 138, 137, 136, //G_3
		135, 134, 133, 132, 131, 130, 129, 128, //Ab3
		127, 126, 125, 125, 124, 123, 122, 121, //A_3
		120, 119, 118, 118, 117, 116, 115, 114, //Bb3
		113, 113, 112, 111, 110, 109, 109, 108, //B_3
		0, //___
		-1 //UNKNOWN;
Ich hab' keine Ahnung, was passiert, wenn man die Werte ändert (bis auf die Tatsache, dass es sich schrecklich anhört) aber evtl. findest du ja jemanden, der darüber genaueres weiss.
 

Förster44

Mitglied
Bitte entschuldigt, wenn ich jetzt viele Anfängerfragen stelle, was meine Kenntnisse des (recht großen) Java-Klassen-Angebots betrifft, bin ich ein blutiger Anfänger :S

Uhrzeitbedingt hab' ich's jetzt nicht ganz nachvollzogen, aber ... ist das irgendwie Performancekritisch?
Um die Performance hab ich mir vorerst weniger Gedanken gemacht, erstmal wollte ich den Algorithmus in einen funktionierenden Zustand bringen^^

Ich hatte bei meinen Experimenten dann irgendwann genug von Linker Kanal, Rechter Kanal, 1 oder 2 byte little oder big endian.... und hab' die Sounddaten dann einfach in einen FloatBuffer reingerechnet (der natürlich bei Bedarf auch wieder in eine abspielbare Form gebracht werden kann).
Nach kurzem Googlen: Warum FloatBuffer (und warum float statt int und Konsorten)? Was ist da anders als bei einem "gewöhnlichen" Array? Welche Vorteile (außer evt. der Performance) bietet das? Hilft der FloatBuffer beim Verändern/Skalieren der Werte?

Ganz naiv würde ich ja vermuten, dass man durch die Welle auch eine interpolierende Funktion durchlegen kann, und damit in bezug auf schnelleres oder langsameres Abspielen recht flexibel sein dürfte.
Ist "durch die Welle" hier eine mir unbekannte Redewendung oder eine mir unbekannte Programmfunktionalität?

Viel wichtiger aber ist, das er sich diese ganzen Array-Operationen (vergrössern, kopieren usw.) sparen kann, wenn er als Positionszeiger auf die Samples ein float verwendet, dann kann er mit der Formel:
Code:
(MAXFREQ / Periodendauer) / SAMPLERATE
diese Position inkremental verändern und die gecasteten ints * FRAMESIZE aus dieser Position zeigen immer auf das richtige Byte im original Array.

Das verstehe ich grade nicht so recht ... ein float als Positionszeiger (geht das nicht nur mit Ganzzahlen)? Welche Komponente der Formel wird inkrementiert? Und wo kommt die Modifizierung zum Einsatz?
 

Marco13

Top Contributor
Ein float[] Array würde natürlich auch gehen. Ein FloatBuffer kann manchmal ganz praktisch sein, und fügt sich schön ins Bild, dass man die Umwandlung bzw. Interpretation der Eingabedaten auch gut mit ByteBuffer bzw. ShortBuffer abhandeln kann. Wenn man eine WAV liest, dann hat die ja (abhängig vom genauen AudioFormat) z.B. 2 bytes pro Frame. Diese 2 bytes liest man aus dem AudioInputStream. Die kann man z.B. in einen ByteArrayOutputStream reinlesen, vom dem man später einen byte[]-Array abholen kann, der alle Daten enthält. Diesen byte[] array kann man mit ByteBuffer#wrap in einen ByteBuffer packen, und dann mit byteBuffer.asShortBuffer() als ShortBuffer ansehen. DER enthält dann wiederum genau die Daten, die du jetzt (etwas unpassenderweise) als int[] array gespeichert hast. Man spart sich damit die manuelle Umwandlung der einzlenen bytes mit den shift>>>operatoren und so.

"Durch die Welle" bezog sich darauf, dass ich (immernoch) glaube, dass durch diese Daten (sei es nun byte, short oder float) eine Kurve beschrieben wird.

Achtung, was folgt ist Halbwissen und naives Wunschdenken, ich fräse mich da gerade erst rein. Wenn irgendwas grob falsch ist, möge man (Spacerat z.B.) mich darauf hinweisen:

Wenn diese Werte also z.B. diese sind:
2, 8, 15, 43, 21, 12, 3
dann entspricht einer Kurve die kurz ansteigt und wieder abfällt - ähnlich wie der erste Teil einer Sinuskurve. Die Zeitpunkte, für die diese Werte vorliegen, hängen von der Abtastrate ab, also bei 22050 liegen die Werte "1/22050" Sekunden auseinander. Wenn man dort nun eine Funktion f durchlegt, so dass
f(0*dt) = 2
f(1*dt) = 8
f(2*dt) = 15
...
(wobei dt='1/22055')
dann hindert einen niemand daran, das ganze schneller abzuspielen, indem man zum eigentlichen Abspielen nicht die Daten f(t) verwendet, sondern die von f(t*speed). Dann bekommt man z.B.
f(0*dt*1.5) = 2
f(1*dt*1.5) = 13
f(2*dt*1.5) = 36
also eine Kurve, die schneller ansteigt.

So, bei Gelegenheit werde ich mal drüber nachdenken (und vielleicht ausprobieren!) ob das stimmt und Sinn macht, was ich hier gerade geschrieben habe ;)
 

Förster44

Mitglied
Ein float[] Array würde natürlich auch gehen. Ein FloatBuffer kann manchmal ganz praktisch sein, und fügt sich schön ins Bild, dass man die Umwandlung bzw. Interpretation der Eingabedaten auch gut mit ByteBuffer bzw. ShortBuffer abhandeln kann. Wenn man eine WAV liest, dann hat die ja (abhängig vom genauen AudioFormat) z.B. 2 bytes pro Frame. Diese 2 bytes liest man aus dem AudioInputStream. Die kann man z.B. in einen ByteArrayOutputStream reinlesen, vom dem man später einen byte[]-Array abholen kann, der alle Daten enthält. Diesen byte[] array kann man mit ByteBuffer#wrap in einen ByteBuffer packen, und dann mit byteBuffer.asShortBuffer() als ShortBuffer ansehen. DER enthält dann wiederum genau die Daten, die du jetzt (etwas unpassenderweise) als int[] array gespeichert hast. Man spart sich damit die manuelle Umwandlung der einzlenen bytes mit den shift>>>operatoren und so.


Was mein bisheriges Ausprobieren mit den Buffern betrifft, hab ich in ShortBuffer keine Möglichkeit gefunden, das byte[] "wiederzubekommen" (mit dem Modifizieren warte ich normalerweise, bis die Konvertierung und das Drumherum stimmen)... außerdem waren auch im ShortBuffer negative Werte drin, ich meine gelesen zu haben dass negative Werte nicht so erwünscht sind?

(Danke übrigens auch für deine Tipps zum Modifizieren, wie gesagt melde ich mich dazu zurück wenn das Drumherum passt ;) )
 
S

Spacerat

Gast
Wie du das ByteArray widerbekommst? Gar nicht, du musst es nur als Instanz behalten. Selbiges gilt für den Byte- und den ShortBuffer. Letztere beiden teilen sich nämlich durch ihre Instanzierung per wrap, das ByteArray. Änderungen im Array bedeuten Änderungen in den Buffern und umgedreht. Das einzige um was du dir noch gedanken machen musst ist die ByteOrder (order = Reihenfolge) der Buffer, damit die Bytes auch in der richtigen Reihenfolge das Short ergeben.
Im übrigen ist Marcos Formel ([c]f(sample * sampleTime * speed)[/c] zum pitchen völlig ausreichend bzw. sogar besser, weil stufenlos. Bei der Protracker-Formel hingegen hat man die Frequenzabstufungen der jeweiligen Noten. Bei Marcos Formel muss man aber aufpassen, dass speed nicht 0 wird.
 

Förster44

Mitglied
Wie du das ByteArray widerbekommst? Gar nicht, du musst es nur als Instanz behalten.
Ah, gut zu wissen!

Das einzige um was du dir noch gedanken machen musst ist die ByteOrder (order = Reihenfolge) der Buffer, damit die Bytes auch in der richtigen Reihenfolge das Short ergeben.
Was ist denn die richtige Reihenfolge? (Beim ersten Anschauen der Buffer nahm ich diese Reihenfolge:
Java:
static byte[] pitch(byte[] toPlay, float var)
{
    ByteBuffer byteBuffer = ByteBuffer.wrap(toPlay);
    ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
}
Ich hoffe dass das so nicht richtig war, weil mich sonst die negativen Werte im ausgegebenen ShortBuffer sehr wundern würden...

Was Marcos Formel angeht, muss ich leider gestehen, dass ich nicht so recht verstehe was sie macht (bzw. wie sie das machen soll) (und wo man dt herbekommt, aber das ist Nebensache), ich hoffe ihr könnt mir das noch etwas genauer erklären ... ?
 
S

Spacerat

Gast
Okay... Grundlagen:
1. In deinem AudioStream (dem ByteArray) sind die Daten ja in einer bestimmten Reihenfolge abgelegt: (r = rechts, l = linkks)
Code:
{r0, l0, r1, l1, ... rN, lN}
2. Bei 16Bit-Samples benötigt man pro Sample also schon mal 2 Bytes: (l = low, h = high)
Code:
{rl0, rh0, ll0, lh0, rl1, rh1, ll1, lh1, ... rlN, rhN, llN, lhN}
3. Low- und Highbytes können pro Kanal nun auch vertauscht sein, das nennt sich ByteOrder Little.Endian oder BigEndian:
Code:
{rh0, rl0, lh0, ll0, rh1, rl1, lh1, ll1, ... rhN, rlN, lhN, llN}
4. Zuletzt ist noch interessant, ob die Daten im Stream Vorzeichenbehaftet sind oder nicht bzw. ob sie gar ein ganz anderes Encoding ausser PCM, nämlich ALAW oder ULAW haben.
5. Zum Abspielen des Streams benötigt man nun noch die Abspielgeschwindigkeit in Samples pro Sekunde (gängig sind 11025Hz, 22050Hz, 44100Hz und 48000Hz; Java untestützt bis zu V1.5 8000Hz bis 48000Hz), das ist die Samplerate. Daraus folgt, dass ein Sample genau 1/SampleRate Sekunden dauert, so kam Marco auch auf dt.

Wie dein AudioStream nun aufgebaut ist, bestimmt dessen AudioFormat ([c]javax.sound.sampled.AudioFormat[/c]). Diese Klasse ist an und für sich selbstredend. Damit lassen sich die AudioStreams individuell interpretieren. Eine Stream entsprechende Instanz davon bekommst du, indem du [c]<Stream>.getAudioFormat()[/c] aufrufst. Man kann also erst mal gar nicht sagen, ob dein Codeschnipsel gerade zufällig deinen Stream korrekt liest. Aber der Ansatz ist schon mal korrekt.
 
Zuletzt bearbeitet von einem Moderator:

Förster44

Mitglied
Danke für diese ausführlichen Informationen ;) Ich hab mir mal das Audioformat ausgeben lassen, sämtliche Kandidaten für die Tonhöhen-Änderungen liegen in folgendem Format vor:
Encoding PCM_SIGNED, Samplerate 16000.0 Hz, 16 bit, Mono, 2 bytes/frame, little-endian

Wie muss ich da dann also das byte[] (oder die Buffer) bearbeiten, um die Daten in weiterverarbeitbare Form zu bringen? (Ich frage hauptsächlich deshalb, weil Marco anfänglich meinte, mit den Shortbuffern könnte ich mir Bitverschiebungen etc. sparen und ich da jetzt etwas verunsichert bin.)
 

Marco13

Top Contributor
Der FloatBuffer sollte dann aber nur Werte zwischen 0 und 1 speichern, nicht -1 bis +1, sonst wird das am Ende erfordeliche Biasing von Unsigned nach Singed ungenau.

Was meintest du damit?

Die Schwierigkeit bei diesen ganzen Sound-Sachen ist, dass man meistens (!) relativ (!) leicht eine Sache für ein bestimmtes Dateiformat schreiben kann. Wenn du mal so eine WAV irgendwo hochlädst, könnte man da mal was testen. Wenn man aber einigermaßen generisch bleiben will, wird es sehr schnell sehr aufwändig. Wenn du also jetzt eine funktionierende Version (mit einer Klasse mit 500 Zeilen) hast, und dann später auch nur eine einzige Datei mit Stereo oder unsigned oder big endian verarbeiten willst, musst du u.U. SEHR große Teile dafür schlicht neu schreiben.
Ich hatte mal angefangen, dafür ein paar Utilties zu schreiben, aber die sind noch ziemlich halbgar...
 
S

Spacerat

Gast
@Marco13: Das N-Flag vorzeichenbehafteter Samples (Zweierkomplement) macht den eigentlich geringeren Anteil der Wellenform zum grösseren. Ferner ist der negative Anteil im Zweierkomplement stets einen Wert grösser. Bei Floats zwischen -1 und +1 würdest du nun zwei Wertebereiche gleicher Länge bekommen, müsstest dich also entscheiden ob du z.B. +128 (ungültiger Wert) hinzunimmst oder -128 weglässt, beides ist sehr ungenau. Deswegen ist's ratsam, den gesamten Wertebereich in den positiven Bereich zu verschieben (+ 128) und ihn dann in ebenso positive Floats zu wandeln (/ 255). Bei der Rückwandlung in ein vorzeichenbehaftetes Format muss das natürlich wieder rückgängig gemacht werden. In der Elektrotechnik nennt sich das Biasing, zu deutsch Arbeitspunkt- bzw. 0-Linienverschiebung.
Eine generische Klasse zum Wandeln beliebiger AudioStreams in FloatBuffer kann ich auch vorweisen.
Java:
import java.io.File;
import java.nio.FloatBuffer;

import static javax.sound.sampled.AudioFormat.Encoding.*;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;


public class AudioTests {
	public static void main(String[] args) throws Exception {
		AudioInputStream ais = AudioSystem.getAudioInputStream(new File(args[0]).getAbsoluteFile());
		AudioFormat af = ais.getFormat();

		if(ALAW.equals(af.getEncoding()) || ULAW.equals(af.getEncoding())) {
			// Encoding ALAW und ULAW in PCM_Signed wandeln
			af = new AudioFormat(PCM_SIGNED,
					af.getSampleRate(),
					af.getSampleSizeInBits() * 2,
					af.getChannels(),
					af.getFrameSize() * 2,
					af.getFrameRate(),
					af.isBigEndian());
			// AudioInputStream durch die A- bzw. ULAW-Decoder lesen.
			ais = AudioSystem.getAudioInputStream(af, ais);
		}
		byte[] sample = new byte[af.getSampleSizeInBits() / 8];
		int sampleBytes = sample.length - 1;
		int mask = (int) Math.pow(2.0, af.getSampleSizeInBits()) - 1;
		int channels = af.getChannels();
		int bias = 0;
		long length = ais.getFrameLength();
		if(length > Integer.MAX_VALUE) {
			length = Integer.MAX_VALUE;
		}
		if(PCM_SIGNED.equals(af.getEncoding())) {
			bias = (int) Math.pow(2.0, af.getSampleSizeInBits() - 1.0);
		}
		int value;
		FloatBuffer[] channelBuffer = new FloatBuffer[af.getChannels()];
		for(int l = 0; l < length; l++) {
			for(int c = 0; c < channels; c++) {
				if(channelBuffer[c] == null) {
					channelBuffer[c] = FloatBuffer.allocate((int) length);
				}
				value = 0;
				ais.read(sample);
				for(int s = 0; s <= sampleBytes; s++) {
					value <<= 8;
					value |= (((af.isBigEndian())? sample[sampleBytes - s] : sample[s]) & 0xFF);
				}
				value = (value + bias) & mask;
				channelBuffer[c].put(value / (float) mask);
			}
		}
	}
}
Hoffe ich hab' da nichts vergessen oder verwechselt. ;)
 
Zuletzt bearbeitet von einem Moderator:

Förster44

Mitglied
Vielen Dank! Es würde mich nur interessieren, wo man in deinem Codebeispiel dann letztendlich (nach dem Modifizieren*) das abzuspielende byte[] herbekommt?


* dazu hätte ich auch noch eine Bitte ... Marcos Funktion f weiter oben scheint dazu ja gut geeignet zu sein, nur habe ich deren Funktionsweise (also wie die Funktion das machen soll) nicht so recht nachvollziehen können, wenn ihr mir das vielleicht noch erklären könntet ... würde mich sehr freuen ;)
 
Zuletzt bearbeitet:
S

Spacerat

Gast
Zunächst erstmal, den FloatBuffer benötigst du nur, wenn du zwischen zwei Amplitudenabgriffen (oder bleiben wir schlicht bei Samples ;)) interpolieren willst, was nebenbei gesagt so ab 20kHz stetig zunehmend weniger Sinn macht (je höher die SampleRate wird, desto weniger Sinn macht's).
Ansonten würde deine Lösung mit Byte- und ShortBuffer schon hinhauen. Nur, warum du immer ein neues ByteArray mit der gestreckten bzw. gestauchten Grösse erstellen willst erschliesst sich mir nicht. Normalerweise arbeitet man in der Praxis mit zwei Puffern fester Grösse. Einer davon am Dateneingang, welcher mit dem Audiostream verknüpft ist und bei Bedarf vom Sampler aktualisiert (maw: weitergelesen) wird, der andere am Datenausgang, welcher mit einer DataLine verknüpft ist, die sofort blockiert, wenn die Hardwarepuffer voll sind. Als Eingangspuffer einer Line darf auch der Ausgangspuffer der Quelle verwendet werden, so reduziert man die Anzahl der in der Line zu definierenden Puffer auf nun mehr einen. Der Sampler bekommt einen Zeiger auf die jeweilige Sampleposition im Float-Format, welcher mittels Marcos oder meiner Formel inkrementiert wird, wobei er logischerweise auch zwischen zwei Abgriffen der Original-Wellenform landen kann. Hier hat man nun die wahl: Schreibe ich erneut den letzten Amplitudenwert in den Ausgangspuffer oder interpoliere ich anteilig zwischen der letzten und folgenden Amplitude und wenn ja, linear oder polynomial (verd. schreibt man das so? :autsch:).
In der Hoffnung, dass du dich schon mal mit Lines (SDLs, TDLs, Mixer und Ports), AudioStreams und Controls auseinander gesetzt hast biete ich dir hier vorläufig erst mal die Klasse LinePitcher an, welche man mit der SDL instanzieren kann, in die man sonst den AudioStream direkt spoolen würde. Interpoliert wird darin aber noch nicht.
Java:
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.Control;
import javax.sound.sampled.Control.Type;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;


public class LinePitcher implements SourceDataLine {
	private static final class PitchControl extends FloatControl {
		protected PitchControl() {
			super(Type.SAMPLE_RATE, 0.0F, 2.0F, Float.MIN_VALUE, 10, 1.0F, "", "-", "+", "0");
		}
		
	}
	private final SourceDataLine source;
	private final List<Control> controls;
	private final PitchControl ctrl;
	private ByteBuffer bOut;
	private float samplePos, stepRate;
	private int frameSize;

	public LinePitcher(SourceDataLine source) {
		this.source = source;
		List<Control> controls = new ArrayList<Control>(Arrays.asList(source.getControls()));
		ctrl = new PitchControl();
		controls.add(ctrl);
		this.controls = Collections.unmodifiableList(controls);
	}

	@Override
	public void drain() {
		synchronized(source) {
			source.drain();
		}
	}

	@Override
	public void flush() {
		synchronized (source) {
			source.flush();
		}
	}

	@Override
	public void start() {
		synchronized (source) {
			source.start();
		}
	}

	@Override
	public void stop() {
		synchronized (source) {
			source.stop();
		}
	}

	@Override
	public boolean isRunning() {
		return source.isRunning();
	}

	@Override
	public boolean isActive() {
		return source.isActive();
	}

	@Override
	public AudioFormat getFormat() {
		return source.getFormat();
	}

	@Override
	public int getBufferSize() {
		return source.getBufferSize();
	}

	@Override
	public int available() {
		return source.available();
	}

	@Override
	public int getFramePosition() {
		return source.getFramePosition();
	}

	@Override
	public long getLongFramePosition() {
		return source.getLongFramePosition();
	}

	@Override
	public long getMicrosecondPosition() {
		return source.getMicrosecondPosition();
	}

	@Override
	public float getLevel() {
		return source.getLevel();
	}

	@Override
	public javax.sound.sampled.Line.Info getLineInfo() {
		return source.getLineInfo();
	}

	@Override
	public void open() throws LineUnavailableException {
		source.open();
	}

	@Override
	public void close() {
		source.close();
	}

	@Override
	public boolean isOpen() {
		return source.isOpen();
	}

	@Override
	public Control[] getControls() {
		return controls.toArray(new Control[controls.size()]);
	}

	@Override
	public boolean isControlSupported(Type control) {
		for(Control c : controls) {
			if(c.getType() == control) {
				return true;
			}
		}
		return false;
	}

	@Override
	public Control getControl(Type control) {
		for(Control c : controls) {
			if(c.getType() == control) {
				return c;
			}
		}
		return null;
	}

	@Override
	public void addLineListener(LineListener listener) {
		source.addLineListener(listener);
	}

	@Override
	public void removeLineListener(LineListener listener) {
		source.removeLineListener(listener);
	}

	@Override
	public void open(AudioFormat format, int bufferSize) throws LineUnavailableException {
		synchronized (source) {
			source.open(format, bufferSize);
			bOut = ByteBuffer.wrap(new byte[bufferSize]);
			frameSize = format.getChannels() * ((format.getSampleSizeInBits() + 7) / 8);
			stepRate = 1.0f / format.getSampleRate(); // Das ist Marcos "dt"
			samplePos = 0.0f;
		}
	}

	@Override
	public void open(AudioFormat format) throws LineUnavailableException {
		int bufferSize = format.getChannels() * ((format.getSampleSizeInBits() + 7) / 8);
		bufferSize *= (int) (format.getSampleRate() / 25.0f) * format.getFrameSize();
		open(format, bufferSize);
	}

	@Override
	public int write(byte[] b, int off, int len) {
		synchronized(source) {
			ByteBuffer bIn = ByteBuffer.wrap(b, off, len);
			samplePos %= 1.0f;
			while(samplePos < len + 1) {
				for(int c = 0; c < frameSize; c++) {
					bOut.put(bIn.get((int) samplePos + c + off));
				}
				if(bOut.position() == bOut.capacity()) {
					source.write(bOut.array(), 0, bOut.capacity());
					bOut.clear();
				}
				samplePos += (stepRate * ctrl.getValue()); // und das ist der Rest von Marcos Formel
				if(samplePos % frameSize >= 1.0f) {
					samplePos += frameSize - 1;
				}
			}
			source.write(bOut.array(), 0, bOut.capacity());
			bOut.clear();
			return len;
		}
	}
}
Dieser LinePitcher ist leider noch nicht getestet, sollte aber funktionieren, wenn sich kein Flüchtigkeitsfehler eingeschlichen hat. Ich werde dazu auf jeden Fall noch ein kleines Demo veröffentlichen.
 
Zuletzt bearbeitet von einem Moderator:
S

Spacerat

Gast
Eigentlich EDIT:
Okay... ein Mal Flüchtigkeit wurde schon mal entlarvt... die "stepRate" ist iwie viel zu kurz. :oops: Mit Marcos Formel komm' ich da nicht hin... hmm... :(
 
S

Spacerat

Gast
Okay... got it
Java:
import java.io.File;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static javax.sound.sampled.AudioFormat.Encoding.*;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Control;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.Control.Type;

public class AudioTests {
	public static void main(String[] args) throws Throwable {
		if(args == null || args.length == 0) {
			args = new String[] {"test.mp3"}; // javazoom MP3 required!
		}
		AudioInputStream ais = ensurePCM(AudioSystem.getAudioInputStream(new File(args[0]).getAbsoluteFile()));
		AudioFormat af = ais.getFormat();
		LinePitcher lp = new LinePitcher(AudioSystem.getSourceDataLine(af));
		lp.open();
		lp.start();
		FloatControl pitch = (FloatControl) lp.getControl(LinePitcher.PITCH_CONTROL);
		FloatControl volume = (FloatControl) lp.getControl(FloatControl.Type.MASTER_GAIN);
		pitch.setValue(1.2f); // play @120% speed
		volume.setValue(volume.getMaximum()); // max volume
		int r = 0;
		byte[] buf = new byte[8192];
		while((r = ais.read(buf)) != -1) {
			lp.write(buf, 0, r);
		}
	}

	public static final AudioInputStream ensurePCM(AudioInputStream source)
	{
		AudioFormat af = source.getFormat();
		Encoding enc = af.getEncoding();
		if(!PCM_SIGNED.equals(enc) && !PCM_UNSIGNED.equals(enc)) {
			int ssb = af.getSampleSizeInBits();
			int c = af.getChannels();
			if(ssb < 8) {
				ssb = 8;
			}
			int minFs = c * ssb / 8;
			int fs = af.getFrameSize();
			if(fs < minFs) {
				fs = minFs;
			}
			af = new AudioFormat(
					AudioFormat.Encoding.PCM_SIGNED,
					af.getSampleRate(),
					ssb * 2,
					af.getChannels(),
					fs * 2,
					af.getSampleRate(),
					af.isBigEndian()
				);
			source = AudioSystem.getAudioInputStream(af, source);
		}
		return source;
	}
}

class LinePitcher implements SourceDataLine {
	public static final PitchControl.Type PITCH_CONTROL = new PitchControl.Type();
	private static final class PitchControl extends FloatControl {
		protected PitchControl() { // a control to pitch an audio stream from 0 (stop) to 2 * norm speed.
			super(PITCH_CONTROL, 0.0F, 2.0F, Float.MIN_VALUE, 10, 1.0F, "", "-", "+", "0");
		}

		private static final class Type extends FloatControl.Type {
			private Type() {
				super("Pitch Control");
			}
		}
	}
	private final Object lock = new Object();
	private final SourceDataLine source;
	private final List<Control> controls;
	private final PitchControl ctrl;
	private ByteBuffer bOut;
	private float samplePos;
	private int frameSize;

	public LinePitcher(SourceDataLine source) {
		this.source = source;
		List<Control> controls = new ArrayList<Control>(Arrays.asList(source.getControls()));
		ctrl = new PitchControl();
		controls.add(ctrl);
		this.controls = Collections.unmodifiableList(controls);
	}

	@Override
	public void drain() {
		synchronized(lock) {
			source.drain();
		}
	}

	@Override
	public void flush() {
		synchronized(lock) {
			source.flush();
		}
	}

	@Override
	public void start() {
		synchronized(lock) {
			source.start();
		}
	}

	@Override
	public void stop() {
		synchronized(lock) {
			source.stop();
		}
	}

	@Override
	public boolean isRunning() {
		return source.isRunning();
	}

	@Override
	public boolean isActive() {
		return source.isActive();
	}

	@Override
	public AudioFormat getFormat() {
		return source.getFormat();
	}

	@Override
	public int getBufferSize() {
		return source.getBufferSize();
	}

	@Override
	public int available() {
		return source.available();
	}

	@Override
	public int getFramePosition() {
		return source.getFramePosition();
	}

	@Override
	public long getLongFramePosition() {
		return source.getLongFramePosition();
	}

	@Override
	public long getMicrosecondPosition() {
		return source.getMicrosecondPosition();
	}

	@Override
	public float getLevel() {
		return source.getLevel();
	}

	@Override
	public javax.sound.sampled.Line.Info getLineInfo() {
		return source.getLineInfo();
	}

	@Override
	public void open() throws LineUnavailableException {
		open(source.getFormat());
	}

	@Override
	public void close() {
		source.close();
	}

	@Override
	public boolean isOpen() {
		return source.isOpen();
	}

	@Override
	public Control[] getControls() {
		return controls.toArray(new Control[controls.size()]);
	}

	@Override
	public boolean isControlSupported(Type control) {
		for(Control c : controls) {
			if(c.getType() == control) {
				return true;
			}
		}
		return false;
	}

	@Override
	public Control getControl(Type control) {
		for(Control c : controls) {
			if(c.getType() == control) {
				return c;
			}
		}
		return null;
	}

	@Override
	public void addLineListener(LineListener listener) {
		source.addLineListener(listener);
	}

	@Override
	public void removeLineListener(LineListener listener) {
		source.removeLineListener(listener);
	}

	

	@Override
	public void open(AudioFormat format, int bufferSize) throws LineUnavailableException {
		synchronized(lock) {
			source.open(format, bufferSize);
			bOut = ByteBuffer.wrap(new byte[bufferSize]);
			frameSize = format.getChannels() * ((format.getSampleSizeInBits() + 7) / 8);
			samplePos = 0.0f;
		}
	}

	@Override
	public void open(AudioFormat format) throws LineUnavailableException {
		int bufferSize = format.getChannels() * ((format.getSampleSizeInBits() + 7) / 8);
		bufferSize *= (int) (format.getSampleRate() / 25.0f) * format.getFrameSize();
		open(format, bufferSize);
	}

	@Override
	public int write(byte[] b, int off, int len) {
		synchronized(lock) {
			ByteBuffer bIn = ByteBuffer.wrap(b, off, len);
			samplePos %= 1.0F;
			int p = 0;
			while(p < len) {
				for(int c = 0; c < frameSize; c++) {
					bOut.put(bIn.get(p + c + off));
				}
				if(bOut.position() == bOut.capacity()) {
					byte[] bb = bOut.array().clone();
					source.write(bb, 0, bOut.capacity());
					bOut.clear();
				}
				samplePos += ctrl.getValue();
				p = (int) samplePos * frameSize;
			}
			b = bOut.array().clone();
			source.write(b, 0, bOut.position());
			bOut.clear();
			return len;
		}
	}
}
Die Werte der Controls der gesamten SoundAPI lassen sich im übrigen auch bequem über GUIs steuern... und jetzt bau' ich mir DJ-Software in Java :lol:
 

Förster44

Mitglied
So, jetzt mach ich mich auch mal ans Ausprobieren deines Codes (danke nochmals dafür übrigens ;) ) ...
Warum ist nun eigentlich "plötzlich" .mp3 das AudioFormat der Wahl? Ich dachte .mp3 wird von der Java Sound API gar nicht unterstützt?
 

Förster44

Mitglied
Ertappt! Du liest keine Kommentare. :lol: javazoom MP3 required heisst
MP3 library for the Java Platform benötigt.

Doch, den Kommentar hab ich gelesen, aber das war gestern Abend und da kam ich nicht auf den Gedanken dass das eine Zusatz-Library sein könnte :oops:

Also zum Thema Zusatz-Library ... da es sich bei jenem Projekt, in dem ich dieses Pitching auch einbauen möchte, um ein Applet handelt, stellt sich mir nun die Frage, wie ich diese Library darin einbinden kann (ich kann in deinem Code auch keine Importe dieser Library erkennen) oder ob jeder, der mein Applet nutzen möchte, sich vorher diese Library installieren muss?
 
S

Spacerat

Gast
Also ich bekomm's zumindest nicht hin, den jLayer in Applets zu nutzen, weder installiert noch in der Website gebundled.
 
S

Spacerat

Gast
'ne wav-datei laden? z.B. XD
Nein, ernsthaft... du musst nur statt "test.mp3" "test.wav" da rein sechreiben, dann lädt er die, sofern es eine "test.wav" gibt.
 
Zuletzt bearbeitet von einem Moderator:

Förster44

Mitglied
'ne wav-datei laden? z.B. XD
Nein, ernsthaft... du musst nur statt "test.mp3" "test.wav" da rein sechreiben, dann lädt er die, sofern es eine "test.wav" gibt.

Achso, okay xD
Ich hatte jetzt gedacht dass dein Code nur mit .mp3 wegen kodierungstechnischer Umstände kompatibel sei, aber umso besser wenns nicht so ist :)
Dann melde ich mich gleich mit den Ergebnissen der Implementierung (in meinem Applet) zurück ;)

Edit: Es funktioniert, wunderbar! Vielen vielen Dank nochmals :toll:
 
Zuletzt bearbeitet:
S

Spacerat

Gast
Juhuuu... Java7 macht alles anders. Zumindest bekomm' ich von einer SourceDataLine keine Volume- bzw. Master-Gain- oder überhaupt irgend eine andere Control mehr. Deswegen funktioniert der Code oben auf Java7 schon mal nicht mehr. Abwärtskompatibilität geht anders. Hat das Oracle etwa aufgegeben? Mal sehen, was nach dem Update noch alles für Überraschungen kommen.
 
S

Spacerat

Gast
Ein Glück... Entwarnung... Die Controls gibt es in Java 7 in der gleichen Form wie eh' und je doch noch, wäre ja verwunderlich, wenn nicht. Allerdings scheinen sie erst verfügbar zu sein, wenn die Line geöffnet wurde.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
C Java3D Sound spielt nur einen Sound ab. Spiele- und Multimedia-Programmierung 8
N Minecraft Frage für einen Minecraft Server Spiele- und Multimedia-Programmierung 2
G Mikrophon-/Audiosteuerung für einen Character Spiele- und Multimedia-Programmierung 1
D Iterieren durch einen Ordner mit Audiodateien und verketten eine andere Audiodatei Spiele- und Multimedia-Programmierung 17
Blender3D VlCJ Windows 10 kein Zugriff auf einen im Netzwerk freigegebnen Ordner Spiele- und Multimedia-Programmierung 2
S Wie kann ich ein Bild in einen Frame einfügen? Spiele- und Multimedia-Programmierung 2
J mehrere Listener für einen Button / Label Spiele- und Multimedia-Programmierung 1
S Einen Dialog zweier Personen erstellen (Textadventure) Spiele- und Multimedia-Programmierung 4
E [LWJGL] Karusell, mehrere Objekte drehen sich um einen Mittelpunkt Spiele- und Multimedia-Programmierung 31
M Einen Hobby Game - / Grafik Designer zu finden (Screenshot vom Spiel) Spiele- und Multimedia-Programmierung 7
F Wie mache ich einen Screenshot vom Display (LWJGL)? Spiele- und Multimedia-Programmierung 7
C Java3D Rotation um einen Punkt (y-achse) Spiele- und Multimedia-Programmierung 2
C JAVA3D Rotation um einen bestimmten Punkt Spiele- und Multimedia-Programmierung 20
Tapsi Anfänger braucht einen Rat ^^ --> Performance Spiele- und Multimedia-Programmierung 7
G Wie bekomme ich einen Punkt (Graphics) Spiele- und Multimedia-Programmierung 2
T Wie kann ich ein OrbitBehavior auf einen festen Wert setzen? Spiele- und Multimedia-Programmierung 2
K Wie malt man am geschicktesten einen Würfel? Spiele- und Multimedia-Programmierung 2
M Anfänger Problem mit einen MouseMotionListener Spiele- und Multimedia-Programmierung 2
G Einen gif oder jpg Datei aus einem Graphics Objekt o. JFrame Spiele- und Multimedia-Programmierung 2
I jPCT rendert nur einen schwarzen Bildschirm Spiele- und Multimedia-Programmierung 2
R Wie zeichne ich einen einzelnen Punkt Spiele- und Multimedia-Programmierung 2
L Sound in Java Spiel Spiele- und Multimedia-Programmierung 5
L Audio-volume /w input Sound.sampled Spiele- und Multimedia-Programmierung 1
D Java sound pulseaudio Spiele- und Multimedia-Programmierung 0
K Sound im loop abspielen, wenn boolscher wert true ist Spiele- und Multimedia-Programmierung 3
M Sound Problem Spiele- und Multimedia-Programmierung 3
M Sound Engin Problem 2 Spiele- und Multimedia-Programmierung 2
J Sound und Musik Spiele- und Multimedia-Programmierung 4
C virtueller stereomix (java sound API) Spiele- und Multimedia-Programmierung 0
I Sound Dateien abspielen Spiele- und Multimedia-Programmierung 9
C Sound einfügen und abspielen Spiele- und Multimedia-Programmierung 6
G Sound-Sampling: Sinuston; kein Fehler, aber auch kein Ton :-( Spiele- und Multimedia-Programmierung 3
M Bilderwechsel in Video an Sound anpassen Spiele- und Multimedia-Programmierung 2
A Problem mit Sound Spiele- und Multimedia-Programmierung 5
F Kein Sound bei javazoom Spiele- und Multimedia-Programmierung 2
J Sound einbinden funktioniert nicht Spiele- und Multimedia-Programmierung 13
S Java Sound-API stagniert Spiele- und Multimedia-Programmierung 16
H Java Sound: WAVE Format17 einlesen. Spiele- und Multimedia-Programmierung 2
Kr0e Java Sound API - DataLine Spiele- und Multimedia-Programmierung 2
S testen ob Sound läuft Spiele- und Multimedia-Programmierung 3
J Windows Sound Aufnehmen Spiele- und Multimedia-Programmierung 10
C Alles hängt, wenn ein Sound abgespielt wird Spiele- und Multimedia-Programmierung 9
K javax.sound.sampled.Clip clone() Spiele- und Multimedia-Programmierung 2
G Ortsabhängiger Sound Spiele- und Multimedia-Programmierung 5
T Sound schneller abspielen Spiele- und Multimedia-Programmierung 4
S Java Sound - Skipping in .mp3 Files und Bug (Bitte überprüfenl) Spiele- und Multimedia-Programmierung 14
G Beste Sound Api Spiele- und Multimedia-Programmierung 2
C Java Sound API Clip.Close() Problem Spiele- und Multimedia-Programmierung 1
Zettelkasten Spezialwiedergabe von Sound vorzeitig beenden Spiele- und Multimedia-Programmierung 3
A Sound Amplitude ermitteln bzw Sound auslöschen Spiele- und Multimedia-Programmierung 4
P Sound auf zwei Lautsprechern getrennt abspielen Spiele- und Multimedia-Programmierung 9
G javafx MediaPlayer sound problem Spiele- und Multimedia-Programmierung 2
Developer_X Sound Lautstärke ohne spezifische API regulieren Spiele- und Multimedia-Programmierung 3
M Java Sound API : Zuordnung Port-Mixer zu Input-Mixer Spiele- und Multimedia-Programmierung 9
P Java Sound OutOfMemoryError Spiele- und Multimedia-Programmierung 2
T Java Sound Probleme Spiele- und Multimedia-Programmierung 8
Pithecanthropus Sound geht nicht Spiele- und Multimedia-Programmierung 6
Dragonfire Java Sound API - Lautstärke ändert sich nicht Spiele- und Multimedia-Programmierung 2
N sound absoielen Spiele- und Multimedia-Programmierung 3
DEvent Java sound Bibliothek Spiele- und Multimedia-Programmierung 11
P Sound Datein mit JAVA zusammenstellen?! Spiele- und Multimedia-Programmierung 3
Developer_X Java3D Point Sound Spiele- und Multimedia-Programmierung 71
B sound mit behavior geht nicht Spiele- und Multimedia-Programmierung 4
M Sound-Lösung für Java/JMF Spiele- und Multimedia-Programmierung 8
0 Sound Spiele- und Multimedia-Programmierung 5
S sound zuverlässig wiedergeben Spiele- und Multimedia-Programmierung 15
G Sound Probleme Spiele- und Multimedia-Programmierung 2
D Sound und Bilddateien vorladen Spiele- und Multimedia-Programmierung 7
R Probleme mit Sound in Applikation. HILFE Spiele- und Multimedia-Programmierung 5

Ähnliche Java Themen

Neue Themen


Oben