Audio de Android - Comportamiento extraño del generador de señales sinusoidales

Cartel de la primera vez aquí. Por lo general, me gusta encontrar la respuesta a mí mismo (ya sea a través de la investigación o de prueba y error), pero estoy perplejo aquí.

Lo que estoy tratando de hacer: Estoy construyendo un sintetizador de audio android simple. En este momento, solo estoy tocando un tono sinusoidal en tiempo real, con un control deslizante en la interfaz de usuario que cambia la frecuencia del tono a medida que el usuario lo ajusta.

Cómo lo he construido: Básicamente, tengo dos subprocesos: un subproceso de trabajo y un subproceso de salida. El subproceso de trabajo simplemente llena un búfer con los datos de onda sinusoidal cada vez que se llama a su método tick (). Una vez que se llena el búfer, alerta al hilo de salida que los datos están listos para escribirse en la pista de audio. La razón por la que estoy usando dos subprocesos es porque audiotrack.write () bloquea, y quiero que el subproceso de trabajo pueda comenzar a procesar sus datos lo antes posible (en lugar de esperar a que la pista de audio termine de escribir). El control deslizante en la interfaz de usuario simplemente cambia una variable en el subproceso de trabajo, de modo que cualquier cambio en la frecuencia (a través del control deslizante) será leído por el método tick () del subproceso de trabajo.

Que funciona: Casi todo; Los subprocesos se comunican bien, no parece haber espacios o clics en la reproducción. A pesar del gran tamaño del búfer (gracias a Android), la capacidad de respuesta está bien. La variable de frecuencia cambia, al igual que los valores intermedios utilizados durante los cálculos del búfer en el método tick () (verificado por Log.i ()).

Lo que no funciona: Por alguna razón, parece que no puedo obtener un cambio continuo en la frecuencia audible. Cuando ajusto el control deslizante, la frecuencia cambia en pasos, a menudo tan amplios como cuartos o quintos. Teóricamente, debería estar escuchando cambios en tan solo 1 Hz, pero no lo estoy. Por extraño que parezca, parece que los cambios en el control deslizante están causando que la onda sinusoidal se reproduzca a través de los intervalos en la serie de armónicos; Sin embargo, puedo verificar que la variable de frecuencia NO se ajusta a los múltiplos integrales de la frecuencia predeterminada.

Mi pista de audio está configurada como tal:

<code>_buffSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
_audioTrackOut = new AudioTrack(AudioManager.STREAM_MUSIC, _sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, _buffSize, AudioTrack.MODE_STREAM);
</code>

El búfer del subproceso de trabajo se está llenando (a través de tick ()) como tal:

<code>public short[] tick()
{
    short[] outBuff = new short[_outBuffSize/2]; // (buffer size in Bytes) / 2
    for (int i = 0; i < _outBuffSize/2; i++) 
    {
        outBuff[i] = (short) (Short.MAX_VALUE * ((float) Math.sin(_currentAngle)));

        //Update angleIncrement, as the frequency may have changed by now
        _angleIncrement = (float) (2.0f * Math.PI) * _freq / _sampleRate;
        _currentAngle = _currentAngle + _angleIncrement;    
    }
    return outBuff;     
}
</code>

Los datos de audio se escriben así:

<code>_audioTrackOut.write(fromWorker, 0, fromWorker.length);
</code>

Cualquier ayuda sería muy apreciada. ¿Cómo puedo obtener cambios más graduales en la frecuencia? Estoy bastante seguro de que mi lógica en tick () es correcta, ya que Log.i () verifica que las variables angleIncrement y currentAngle se actualicen correctamente.

¡Gracias!

Actualizar:

Encontré un problema similar aquí:Problemas de búfer de AudioTrack de Android La solución propuso que uno debe ser capaz de producir muestras lo suficientemente rápido para la pista de audio, lo que tiene sentido. Bajé mi frecuencia de muestreo a 22050Hz y realicé algunas pruebas empíricas: puedo llenar mi búfer (a través de tick ()) en aproximadamente 6 ms en el peor de los casos. Esto es más que adecuado. A 22050Hz, el audioTrack me da un tamaño de búfer de 2048 muestras (o 4096 bytes). Por lo tanto, cada búfer lleno dura ~ 0.0928 segundos de audio, que es mucho más largo de lo necesario para crear los datos (1 ~ 6 ms). SO, sé que no tengo ningún problema en producir muestras lo suficientemente rápido.

También debo tener en cuenta que durante los primeros 3 segundos del ciclo de vida de las aplicaciones, funciona bien: un barrido suave del control deslizante produce un barrido suave en la salida de audio. Después de esto, comienza a volverse realmente entrecortado (el sonido solo cambia cada 100 MHz), y después de eso, deja de responder a la entrada del control deslizante.

También solucioné un error, pero no creo que tenga un efecto. AudioTrack.getMinBufferSize () devuelve el tamaño de búfer permitido más pequeño en BYTES, y estaba usando este número como la longitud del búfer en tick (). Ahora uso la mitad de este número (2 bytes por muestra).

Respuestas a la pregunta(1)

Su respuesta a la pregunta