Problèmes de mise en mémoire tampon Android AudioTrack
Ok, donc j'ai un générateur de fréquence qui utilise AudioTrack pour envoyer des données PCM pour le matériel. Voici le code que j'utilise pour que:
private class playSoundTask extends AsyncTask<Void, Void, Void> {
float frequency;
float increment;
float angle = 0;
short samples[] = new short[1024];
@Override
protected void onPreExecute() {
int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT );
track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
minSize, AudioTrack.MODE_STREAM);
track.play();
}
@Override
protected Void doInBackground(Void... params) {
while( Main.this.isPlaying)
{
for( int i = 0; i < samples.length; i++ )
{
frequency = (float)Main.this.slider.getProgress();
increment = (float)(2*Math.PI) * frequency / 44100;
samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);
angle += increment;
}
track.write(samples, 0, samples.length);
}
return null;
}
}
La fréquence est liée à une barre de délement, et la valeur correcte est signalée dans l'exemple de la génération de la boucle. Tout est bel et bon, quand je démarre l'application. Lorsque vous faites glisser votre doigt le long de la barre de délement vous obtenez un joli balayage sonore. Mais après environ 10 secondes, de jouer avec, l'audio commence à devenir nerveux. Au lieu d'un balayage régulier, il étalés dans le temps, et seuls les changements de tonalité autour de 1000 Hz. Toutes les idées sur ce qui pourrait être la cause?
Voici tout le code dans le cas où le problème est ailleurs:
public class Main extends Activity implements OnClickListener, OnSeekBarChangeListener {
AudioTrack track;
SeekBar slider;
ImageButton playButton;
TextView display;
boolean isPlaying=false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
display = (TextView) findViewById(R.id.display);
display.setText("5000 Hz");
slider = (SeekBar) findViewById(R.id.slider);
slider.setMax(20000);
slider.setProgress(5000);
slider.setOnSeekBarChangeListener(this);
playButton = (ImageButton) findViewById(R.id.play);
playButton.setOnClickListener(this);
}
private class playSoundTask extends AsyncTask<Void, Void, Void> {
float frequency;
float increment;
float angle = 0;
short samples[] = new short[1024];
@Override
protected void onPreExecute() {
int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT );
track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
minSize, AudioTrack.MODE_STREAM);
track.play();
}
@Override
protected Void doInBackground(Void... params) {
while( Main.this.isPlaying)
{
for( int i = 0; i < samples.length; i++ )
{
frequency = (float)Main.this.slider.getProgress();
increment = (float)(2*Math.PI) * frequency / 44100;
samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);
angle += increment;
}
track.write(samples, 0, samples.length);
}
return null;
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
display.setText(""+progress+" Hz");
}
public void onClick(View v) {
if (isPlaying) {
stop();
} else {
start();
}
}
public void stop() {
isPlaying=false;
playButton.setImageResource(R.drawable.play);
}
public void start() {
isPlaying=true;
playButton.setImageResource(R.drawable.stop);
new playSoundTask().execute();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onStop() {
super.onStop();
//Store state
stop();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
//TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//TODO Auto-generated method stub
}
}
source d'informationauteur JCL
Vous devez vous connecter pour publier un commentaire.
J'ai trouvé la cause!
Le problème est la taille de la AudioTrack du tampon. Si vous ne pouvez pas générer des échantillons assez rapide, les échantillons, la lecture s'arrête, et se poursuit lorsqu'il y a suffisamment d'échantillons de nouveau.
La seule solution est de faire en sorte que vous pouvez générer des échantillons assez rapide (44100/s). Comme un fast-fix, essayez de diminuer le taux d'échantillonnage à 22000 (ou l'augmentation de la taille de la mémoire tampon).
Au moins c'est comment il se comporte pour moi - quand j'ai optimisé mon exemple de routine de génération, le saut s'en alla.
(Augmentation de la taille de la mémoire tampon permet au son de commencer à jouer plus tard, comme il y a une réserve d'être attendu - jusqu'à ce que le tampon se remplit. Mais encore, si vous n'êtes pas générer les échantillons assez vite, finalement, les échantillons).
Oh, et vous devriez mettre de l'invariant de variables en dehors de la boucle! Et peut-être faire les échantillons tableau plus grand, de sorte que la boucle s'exécute plus.
Vous pouvez également précalculer les valeurs de tous les sines pour des fréquences de 200 hz-8000 hz, par pas de 10 hz. Ou le faire à la demande: à l'utilisateur de sélectionner une fréquence, de générer des échantillons à l'aide de votre méthode pour un certain temps et aussi de les enregistrer dans un tableau. Lorsque vous générez suffisamment d'échantillons pour avoir une pleine onde sinusoïdale, vous pouvez simplement continuer à boucler sur le tableau (depuis que le péché est une fonction périodique). En fait il pourrait y avoir quelques petites incohérences dans le son comme vous n'avez jamais touché à une période exactement (parce que vous êtes l'augmentation de l'angle par 1 et la longueur d'une onde sinusoïdale est
sampleRate /(double)frequency
échantillons). Mais en prenant un droit multiple de la période du fait de la incohérences imperceptible.Aussi, voir http://developer.android.com/guide/practices/design/performance.html
Au cas où quelqu'un trébuche à travers ce avec le même problème (comme je l'ai fait), la solution est en fait n'a rien à voir avec les tampons, il a à voir avec la fonction sinus. Essayez de remplacer
angle += increment
avecangle += (increment % (2.0f * (float) Math.PI));
. Aussi, pour plus d'efficacité, essayez d'utiliser FloatMath.sin() au lieu de Mathématiques.sin().Je pense que j'ai réussi à résoudre le problème.
Comme indiqué précédemment, il n'est pas sur les tampons. C'est sur l'angle de la variable, il augmente en permanence. Après un certain temps, la variable devient trop grande et ne prend pas en charge des petits pas.
Comme le sinus se répète au bout de 2*PI, nous avons besoin de prendre le module d'angle.
Espère que cette aide.
édité: angle += incrément est assez pour le travail.