Android Audio - Streaming Sinus-Generator ungerade Verhalten

8

Poster zum ersten Mal hier. Normalerweise finde ich die Antwort gerne selbst (sei es durch Recherche oder Versuch-und-Irrtum), aber ich bin hier ratlos.

Was ich versuche zu tun: Ich baue einen einfachen Android-Audio-Synthesizer. Im Moment spiele ich nur einen Sinuston in Echtzeit, mit einem Schieberegler in der Benutzeroberfläche, der die Frequenz des Tons ändert, wenn der Benutzer sie einstellt.

Wie ich es gebaut habe: Grundsätzlich habe ich zwei Threads - einen Worker-Thread und einen Ausgabe-Thread. Der Worker-Thread füllt bei jedem Aufruf der Methode tick () einfach einen Puffer mit den Sinuswellendaten. Sobald der Puffer gefüllt ist, warnt er den Ausgabe-Thread, dass die Daten bereit sind, in die Audiospur geschrieben zu werden. Der Grund, warum ich zwei Threads verwende, ist, dass audiotrack.write () blockiert, und ich möchte, dass der Worker-Thread in der Lage ist, seine Daten so schnell wie möglich zu verarbeiten (anstatt darauf zu warten, dass die Audiospur fertig geschrieben wird). Der Schieberegler auf der Benutzeroberfläche ändert einfach eine Variable im Arbeitsthread, sodass Änderungen an der Häufigkeit (über den Schieberegler) von der Methode tick () des Arbeitsthreads gelesen werden.

Was funktioniert: Beinahe alles; Die Threads kommunizieren gut, es scheint keine Lücken oder Klicks in der Wiedergabe zu geben. Trotz der großen Puffergröße (danke Android) ist die Reaktionszeit in Ordnung. Die Häufigkeitsvariable ändert sich ebenso wie die Zwischenwerte, die während der Pufferberechnungen in der Methode tick () verwendet werden (verifiziert durch Log.i ()).

Was nicht funktioniert: Aus irgendeinem Grund kann ich keine kontinuierliche Änderung der hörbaren Frequenz feststellen. Wenn ich den Schieberegler verstelle, ändert sich die Frequenz in Schritten, oft so breit wie Quinten oder Quinten. Theoretisch sollte ich Veränderungen von 1 Minute hören, aber ich bin es nicht. Seltsamerweise scheint es, als würden Änderungen am Slider die Sinuswelle durch Intervalle in der harmonischen Reihe spielen lassen; Ich kann jedoch überprüfen, dass die Frequenzvariable nicht auf ganzzahlige Vielfache der Standardfrequenz einrastet.

Meine Audiospur ist wie folgt eingerichtet:

%Vor%

Der Puffer des Arbeitsthreads wird (über tick ()) folgendermaßen gefüllt:

%Vor%

Die Audiodaten werden folgendermaßen geschrieben:

%Vor%

Jede Hilfe wird sehr geschätzt. Wie kann ich die Frequenz schrittweise verändern? Ich bin ziemlich sicher, dass meine Logik in tick () korrekt ist, da Log.i () überprüft, ob die Variablen angleIncrement und currentAngle korrekt aktualisiert werden.

Danke!

Aktualisierung:

Ich habe hier ein ähnliches Problem gefunden: Android AudioTrack Pufferprobleme Die Lösung schlug vor, dass man in der Lage sein muss, Samples schnell genug für den audioTrack zu produzieren, was sinnvoll ist. Ich senkte meine Abtastrate auf 22050 Hz und führte einige empirische Tests durch - ich kann meinen Puffer (über tick ()) in ungefähr 6ms im schlimmsten Fall füllen. Das ist mehr als ausreichend. Bei 22050Hz gibt mir der audioTrack eine Puffergröße von 2048 Samples (oder 4096 Bytes). So dauert jeder gefüllte Puffer für ~ 0,0928 Sekunden Audio, was viel länger ist, als es dauert, um die Daten zu erstellen (1 ~ 6 ms). SO, ich weiß, dass ich keine Probleme habe, Proben schnell genug zu produzieren.

Ich sollte auch beachten, dass es für die ersten 3 Sekunden des Anwendungslebenszyklus gut funktioniert - ein reibungsloser Durchlauf des Schiebereglers erzeugt einen sanften Sweep in der Audioausgabe. Danach beginnt es wirklich abgehackt zu werden (der Sound ändert sich nur etwa alle 100 MHz), und danach reagiert es überhaupt nicht mehr auf den Schieberegler.

Ich habe auch einen Fehler behoben, aber ich denke nicht, dass es einen Effekt hat. AudioTrack.getMinBufferSize () gibt die kleinste zulässige Puffergröße in BYTES zurück, und ich habe diese Zahl als Länge des Puffers in tick () verwendet - ich benutze jetzt die Hälfte dieser Zahl (2 Bytes pro Sample).

    
Tsherr 15.04.2012, 00:01
quelle

1 Antwort

4

Ich habe es gefunden!

Es stellt sich heraus, dass das Problem nichts mit Puffern oder Threading zu tun hat.

Es klingt in den ersten paar Sekunden gut, weil der Winkel der Berechnung relativ klein ist. Während das Programm läuft und der Winkel wächst, beginnt Math.sin (_currentAngle) unzuverlässige Werte zu produzieren.

Also habe ich Math.sin() durch FloatMath.sin() ersetzt.

Ich habe auch ersetzt _currentAngle = _currentAngle + _angleIncrement;

mit

_currentAngle = ((_currentAngle + _angleIncrement) % (2.0f * (float) Math.PI)); , so ist der Winkel immer & lt; 2 * PI.

Funktioniert wie ein Zauber! Vielen Dank für Ihre Hilfe, Prätorianer Droid!

    
Tsherr 15.04.2012, 08:23
quelle

Tags und Links