java android audio synthesis

java - Android Audio-Streaming sinusoidal generador de tono de comportamiento impar



synthesis (1)

¡Lo encontré!

Resulta que el problema no tiene nada que ver con buffers o threading.

Suena bien en los primeros segundos, porque el ángulo del cálculo es relativamente pequeño. A medida que el programa se ejecuta y el ángulo crece, Math.sin (_currentAngle) comienza a producir valores no confiables.

Entonces, reemplacé Math.sin() con FloatMath.sin() .

También reemplacé _currentAngle = _currentAngle + _angleIncrement;

con

_currentAngle = ((_currentAngle + _angleIncrement) % (2.0f * (float) Math.PI)); , entonces el ángulo siempre es <2 * PI.

¡Funciona de maravilla! Muchas gracias por su ayuda, ¡robot pretoriano!

primer póster aquí. Por lo general, me gusta encontrar la respuesta yo mismo (ya sea a través de investigación o ensayo y error), pero estoy perplejo aquí.

Lo que estoy tratando de hacer: estoy construyendo un sintetizador de audio android simple. En este momento, estoy reproduciendo 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 hilos: un hilo de trabajo y un hilo de salida. El hilo 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 hilo 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 hilo del trabajador.

Lo que funciona: casi todo; Los hilos 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 escuchar cambios tan pequeños como 1Hz, pero no lo estoy. Por extraño que parezca, parece como si los cambios en el control deslizante están causando que la onda sinusoidal se reproduzca a través de intervalos en la serie de armónicos; Sin embargo, puedo verificar que la variable de frecuencia NO está ajustando a múltiplos enteros de la frecuencia predeterminada.

Mi pista de audio está configurada como tal:

_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);

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

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; }

Los datos de audio se escriben así:

_audioTrackOut.write(fromWorker, 0, fromWorker.length);

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 sólida, ya que Log.i () verifica que las variables angleIncrement y currentAngle se estén actualizando correctamente.

¡Gracias!

Actualizar:

Encontré un problema similar aquí: problemas de almacenamiento en el búfer de Android AudioTrack La solución propuesta es que uno debe ser capaz de producir muestras lo suficientemente rápido para el audioTrack, lo cual tiene sentido. Bajé mi frecuencia de muestreo a 22050 Hz y realicé algunas pruebas empíricas: puedo llenar mi memoria intermedia (mediante 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 buffer de 2048 muestras (o 4096 Bytes). Por lo tanto, cada memoria intermedia llena dura ~ 0.0928 segundos de audio, que es mucho más larga de lo que lleva crear los datos (1 ~ 6 ms). ASÍ, sé que no tengo ningún problema para producir muestras lo suficientemente rápido.

También debo señalar 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 ponerse muy picado (el sonido solo cambia cada 100Mhz), 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).