Android Audio – Transmisión de generador de seno-tono comportamiento extraño
Cartel de la primera vez aquí. Normalmente me gusta encontrar la respuesta yo mismo (ya sea a través de la investigación o ensayo y error), pero estoy perplejo aquí.
Lo que estoy tratando de hacer: Estoy construyendo un simple sintetizador de audio androide. En este momento, sólo estoy jugando un sine-tone en tiempo real, con un control deslizante en la interfaz de usuario que cambia la frecuencia del tono como el usuario lo ajusta.
- DoInBackground () error en facebook api en android studio
- extendiendo la clase cardview muestran el rectángulo blanco alrededor de la tarjeta
- Preferencias compartidas de Android TinyDB putListObject frunction
- Múltiples elementos seleccionados RecyclerView in Activity.java
- Obtener la duración del mp3 en android
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 senoidal 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 ser escritos en la pista de audio. La razón por la que estoy usando dos subprocesos es porque los bloques audiotrack.write (), y quiero que el hilo de trabajo pueda comenzar a procesar sus datos tan pronto como sea 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.
Lo que funciona: Casi todo; Los hilos se comunican bien, no parece haber ningún hueco o clic en la reproducción. A pesar del tamaño del búfer grande (gracias android), la respuesta es aceptable. La variable de frecuencia cambia, al igual que los valores intermedios utilizados durante los cálculos del buffer en el método tick () (verificado por Log.i ()).
Lo que no funciona: Por alguna razón, no puedo conseguir un cambio continuo en la frecuencia audible. Cuando ajusto el control deslizante, la frecuencia cambia en pasos, a menudo tan anchos como cuartos o quintos. Teóricamente, debería estar escuchando cambios tan pequeños como 1Hz, pero no lo estoy. Curiosamente, parece como si los cambios en el control deslizante está causando que la onda sinusoidal juegue a través de los intervalos en la serie armónica; Sin embargo, puedo verificar que la variable de frecuencia NO se está ajustando a múltiplos enteros de la frecuencia por defecto.
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 hilo de trabajo se está poblando (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 sonido, como Log.i () verifica que las variables angleIncrement y currentAngle se están actualizando correctamente.
¡Gracias!
Actualizar:
He encontrado un problema similar aquí: Android AudioTrack problemas de almacenamiento en búfer La solución que se propone que uno debe ser capaz de producir muestras lo suficientemente rápido para el audioTrack, que tiene sentido. Bajé mi frecuencia de muestreo a 22050Hz, y corrí algunas pruebas empíricas – Puedo llenar mi buffer (vía tick ()) en aproximadamente 6ms 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 que se necesita para crear los datos (1 ~ 6 ms). Por lo tanto, sé que no tengo problemas para 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 deslizador produce un barrido suave en la salida de audio. Después de esto, comienza a conseguir realmente agitado (el sonido cambia solamente sobre cada 100Mhz), y después de eso, deja de responder a la entrada del resbalador en todos.
También he arreglado un error, pero no creo que tenga un efecto. AudioTrack.getMinBufferSize () devuelve el tamaño de búfer permisible 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).
- Restaurar la pila posterior de Android después de cerrar la aplicación
- DoubleClick for Publishers en Google Play Services causa NullPointerException en 4.2.2
- Cómo configurar el color de fondo TabHost
- ¿Cómo funciona el atributo androide Xml android: onClick = "..." trabajo detrás de las escenas?
- Bouncycastle codificación de la curva elíptica en Android
- ¿Cómo puedo obtener el valor entre paréntesis en una cadena
- Intención de youtube app profile / channel
- No hay intención de UserRecoverableAuthIOException con Drive SDK para Android
¡Lo he encontrado!
Resulta que el problema no tiene nada que ver con tampones o enhebrado.
Suena bien en el primer par de segundos, porque el ángulo de la computación es relativamente pequeño. A medida que el programa se ejecuta y el ángulo crece, Math.sin (_currentAngle) comienza a producir valores no fiables.
Por lo tanto, he reemplazado Math.sin()
con FloatMath.sin()
.
También reemplazé _currentAngle = _currentAngle + _angleIncrement;
con
_currentAngle = ((_currentAngle + _angleIncrement) % (2.0f * (float) Math.PI));
, Por lo que el ángulo es siempre <2 * PI.
¡Funciona de maravilla! Muchas gracias por su ayuda, droide pretoriano!
- Se barajaron las imágenes al desplazar un ListView con un ViewHolder
- Ejecutar GTM diagnosticar error