Noticias

¡RECUERDA QUE SI ERES UN NUEVO USUARIO, DEBES PRESENTARTE PARA PODER PUBLICAR MENSAJES! | TENEMOS CANAL OFICIAL DE TELEGRAM: t.me/unity3dspain

Temas recientes

Cargar audio desde el sistema de fichero

Iniciado por Trunex, Enero 30, 2019, 05:08:26 PM

Tema anterior - Siguiente tema
Hola a todos, estoy intentando hacer un pequeño reproductor para que el usuario pueda indicar una ruta de un directorio y mientras que está jugando pueda escuchar de fondo los audios que tenga en dicho directorio. Para solucionar el tipo de los audios (mp3 no es soportado en standalone) uso una variante de la librería NAudio adaptada a Mono. Actualmente me funciona, es capaz de leer los audios y reproducirlos. El problema es que cada vez que se crea el AudioClip (AudioClip.Create) Unity se queda pillado durante 1 segundo. He intentado crear el AudioClip en una hebra y no es posible, el AudioClip solamente se puede crear en la hebra principal. He buscado en la especificación de la api y hay un ejemplo de uso de AudioClip.PCMReaderCallback en el cual crea de forma dinámica una onda senoidal (https://docs.unity3d.com/ScriptReference/AudioClip.Create.html). Yo tengo un float[] en el cual tengo los datos del audio, ese float[] son los datos que antes le pasaba por AudioClip.SetData(), el problema que tengo es que no sé cómo puedo hacer que el float[] que ya tengo lo lea AudioClip.PCMReaderCallback.
 
He probado estas implementaciones y ninguna me ha funcionado, no me dan error pero no se escucha nada cuando lo reproduzco con el AudioSource.
 

   void OnAudioRead(float[] data)
    {
        int count = 0;
        while (count < data.Length)
        {
            data[count] = audio.Datos[count];
            count++;
        }
    }


   void OnAudioRead(float[] data)
    {
        int count = 0;
        int posicion = 0;
        while (count < data.Length)
        {
            data[count] = audio.Datos[posicion];
            count++;
        }
        posicion++;
    }


   void OnAudioRead(float[] data)
    {
         data = audio.Datos;
    }

 
Gracias por cualquier ayuda.

A ver si entendí, ese float[] es el conjunto de datos de las muestras del buffer que ingresan al reproductor de audio (del clip en cuestión) ? o es el total de muestras ? porque en la doc dice las muestras leidas:
 
Delegate called each time AudioClip reads data.
 
Cita de: Trunex date=1548864506He probado estas implementaciones y ninguna me ha funcionado, no me dan error pero no se escucha nada cuando lo reproduzco con el AudioSource.
   


Para mi lo que pasa (quizás, no lo se) es que el arreglo de floats se pasan al delegado por valor y no por referencia, entonces toda modificación que hagas a estos no se ve afectada en el arreglo que se reproduce. Quizás puedas setearle al clip los arreglos propios con el "SetData"(código ejemplo de la doc):
 

clip.SetData(samples, 0);

 
(no tengo idea si está bien, seguramente no )
 

void OnAudioRead(float[] data)
    {
        int count = 0;
        while (count < data.Length)
        {
            data[count] = audio.Datos[count];
            count++;
        }
                                   
        clip.SetData(data , 0);
    }

 
 

Cita de: lightbug date=1548869998A ver si entendí, ese float[] es el conjunto de datos de las muestras del buffer que ingresan al reproductor de audio (del clip en cuestión) ? o es el total de muestras ? porque en la doc dice las muestras leidas:
   


El float[] que obtengo de leer el archivo de audio es el mismo float[] que se pasa por parámetro en clip.SetData(float[], int)
 
Cita de: lightbug date=1548869998Para mi lo que pasa (quizás, no lo se) es que el arreglo de floats se pasan al delegado por valor y no por referencia, entonces toda modificación que hagas a estos no se ve afectada en el arreglo que se reproduce.
   


Por lo que se ve en los parametros se pasa por valor, pero si ves el ejemplo de Unity (https://docs.unity3d.com/ScriptReference/AudioClip.Create.html) modifica es float[] data que se supone que esta pasado por valor.
 
Cita de: lightbug date=1548869998Quizás puedas setearle al clip los arreglos propios con el "SetData"(código ejemplo de la doc)
   


El problema que tengo es que al crear la instancia de AudioClip Unity se queda bloqueado durante 1 segundo, el SetData me funciona perfectamente y es inmediato, el problema es al crear la instancia del AudioClip al cual le voy a pasar los datos, no se si me explico...

Ahh ya entendí, son todas las muestras del buffer, el callback creo que se corre cuando cada vez que se carga parte del clip en el audioSource para reproducción, lo mismo cada vez que necesita cambia de posición del cursor. Sí, en el ejemplo está claro, lo que hace es crear una sinusoidal. La variable "position" la actualiza cada vez que se reproduce para mantener la fase constante (solo sirve en la fase de la sinusoide).
 
Hiciste lo del Start no? de asignar el clip al audioSource y de reproducirlo? si no hiciste esto las llamadas no se realizan.

Cita de: lightbug date=1548873954Ahh ya entendí, son todas las muestras del buffer, el callback creo que se corre cuando cada vez que se carga parte del clip en el audioSource para reproducción, lo mismo cada vez que necesita cambia de posición del cursor. Sí, en el ejemplo está claro, lo que hace es crear una sinusoidal. La variable "position" la actualiza cada vez que se reproduce para mantener la fase constante (solo sirve en la fase de la sinusoide).
   
   
      Hiciste lo del Start no? de asignar el clip al audioSource y de reproducirlo? si no hiciste esto las llamadas no se realizan.
   


Si claro, lo único que no sé es cambiar la función OnAudioRead para que en lugar de crear un sonido constante pueda pasarle (a medida de que me los va pidiendo) los datos del archivo de audio. 

Cita de: Trunex date=1548878167Si claro, lo único que no sé es cambiar la función OnAudioRead para que en lugar de crear un sonido constante pueda pasarle (a medida de que me los va pidiendo) los datos del archivo de audio. 
   


En teoría si especficas el sampleRate y metés (para ese samplerate) los datos muestreados (tu archivo de audio) debería funcionar. A mi se me reproduce lo que le meta, señales square, ruido y demás.
 
Como tenés tu archivo guardado? está convertido a un array de valores?

Cita de: lightbug date=1548884891Como tenés tu archivo guardado? está convertido a un array de valores?
   


Es un mp3 de una canción cualquiera descargado de internet, y al abrirlo pues obtengo los canales, la frecuencia, un float[] con los datos, etc.
 
Cita de: lightbug date=1548884891En teoría si especficas el sampleRate y metés (para ese samplerate) los datos muestreados (tu archivo de audio) debería funcionar. A mi se me reproduce lo que le meta, señales square, ruido y demás.
   


No se si te refieres al método SetData(float[], int) o a la función OnAudioRead, el SetData no lo puedo usar, ya que para poder usar el SetData tengo que crear el AudioClip con la el parámetro stream en false y llamar a ese método Create me bloquea el Unity durante 1 segundo, entonces lo único que puedo hacer es crear el AudioClip  con el parámetro stream en true y pasarle los datos por la función OnAudioRead así no se me quedaría bloqueado el juego; si te refieres a cómo debo implementar el OnAudioRead, ¿podrías enseñarme el código o un esquema de dicha implementación si no es mucha molestia?

Cita de: Trunex date=1548885598No se si te refieres al método SetData(float[], int)
   


No, me refería al mismo AudioClip.Create:
 
public static AudioClip Create(string name, int lengthSamples, int channels, int frequency, bool stream, AudioClip.PCMReaderCallback pcmreadercallback, AudioClip.PCMSetPositionCallback pcmsetpositioncallback);
 
Cita de: Trunex date=1548885598¿podrías enseñarme el código o un esquema de dicha implementación si no es mucha molestia?
   


En realidad no hice nada que no esté en el ejemplo, lo unico me fabriqué mis 44000 floats
 

float[] samples = new float[44000] ;

 
en start fui creando variaciones y probando ondas, triangulares, cuadradas, etc, lo más simple de todo sería generar un Random.Range( 0f , 1f ) de prueba y ver si reproduce algo, podés meterlo en el callback pero te da un warning de que Random debería correr en el hilo principal (no se), funciona igual, pero con eso estás fabricando la señal (tipo un sistetizador) y vos lo que querés es partir de datos ya almacenados.
 
Cita de: Trunex date=1548885598¿podrías enseñarme el código o un esquema de dicha implementación si no es mucha molestia?
   


Claro, no tengo la gran cosa, es solo para hacer pruebas, se escucha algo raro (algunos clicks) seguramente sea el position y el arreglo ... pero basicamente creás (en modo LOOP) la curva de animación que quieras, el script te crea los datos (los floats) y el OnAudioRead los lee/modifica
 

public AnimationCurve wave = AnimationCurve.Linear(0,1,1,0);
    AudioSource audioS;
    float[] waveData;
    int position = 0;
    public int sampleRate = 44100;
    public int targetFreq = 1000;
    void Start()
    {
        int samplesPerPeriod = sampleRate / targetFreq;
        waveData = new float[sampleRate];        
        int sample = 0;
        float waveCursor = 0;
        float waveCursorStep = (float)targetFreq / ( sampleRate);
        while(sample < waveData.Length)
        {                  
            waveData[sample] = Mathf.Clamp01(wave.Evaluate(waveCursor));// + 0.1f * Random.Range(0 , 1f);
            waveCursor += waveCursorStep;
            sample++;
        }        
        AudioClip myClip = AudioClip.Create("Wave", sampleRate , 1, sampleRate / 2, false, OnAudioRead, OnAudioSetPosition);
        AudioSource aud = GetComponent<AudioSource>();
        aud.clip = myClip;
        aud.Play();
    }
    void OnAudioRead(float[] data)
    {
        int count = 0;
        while (count < data.Length)
        {
            data[count] = waveData[position + count] ;            
            count++;
        }
    }
    void OnAudioSetPosition(int newPosition)
    {
        position = newPosition;
    }

 
 
 
 

Cita de: lightbug date=1548892611Claro, no tengo la gran cosa, es solo para hacer pruebas, se escucha algo raro (algunos clicks) seguramente sea el position y el arreglo ... pero basicamente creás (en modo LOOP) la curva de animación que quieras, el script te crea los datos (los floats) y el OnAudioRead los lee/modifica
   


He probado su código, y funciona perfectamente, luego he cambiado los parámetros para adaptarlo a un fichero mp3 auténtico y me sigue sin reproducir ningún sonido.
 
Estos son los datos obtenidos del fichero:
 

Debug.Log("LengthSamples: " + audioFichero.Length); //78354432
Debug.Log("Frecuency: " + audioFichero.WaveFormat.SampleRate); //48000
Debug.Log("Datos audio: " + datosAudio.Length); //78354432
Debug.Log("Canales: " + audioFichero.WaveFormat.Channels); //2

 
Entonces, audioFichero es el contenedor que me devuelve la librería al abrir el archivo mp3, de este contenedor obtengo en orden: lengthSamples, frecuency y channels; datosAudio es el float[] con el contenido del audio (el equivalente a waveData  en su código). ¿Por casualidad tiene alguna idea de qué puede estar fallando?

Cita de: Trunex date=1548897464¿Por casualidad tiene alguna idea de qué puede estar fallando?
   


lo primero que se me viene a la cabeza es que en el script que estamos usando reproducimos datos crudos, osea un pcm (raw). Para el caso del mp3 (no se internamente exactamente que hace) pero se que miente, osea, el mp3 es el jpeg del audio, necesitas traducirlo nuevamente a datos reales. No se en realidad que es lo que estás metiendo en el archivo que estás usando, si son datos crudos, datos + metadata, etc. En conclusión, no se si debés decodificar el mp3 antes de poder usarlo o no(si es que la herramienta que estás usando te permite eso, que segun veo de github lo permite pero no se si lo estás usando como se debe)
 
Estuve leyendo esta parte de la doc (no se si es exactamente lo que usas):
 


Decompressing Frames


 
Once you have parsed a complete MP3 frame, it is time to decompress it so we can buffer the PCM audio. NAudio includes a choice of two MP3 frame decompressors, one using the ACM codecs, which should be present on Windows XP onwards, and one for using the DMO (DirectX Media Object) decoder, which is available with Windows Vista onwards. These two classes were internal with NAudio 1.4, but will be made public for NAudio 1.5. You could alternatively use a fully managed MP3 decoder, such as NLayer which is my C# port of the Java MP3 decoder JavaLayer. If I get time I will update NAudioDemo to allow you to select between the available frame decompressors.

Cita de: lightbug date=1548920146lo primero que se me viene a la cabeza es que en el script que estamos usando reproducimos datos crudos, osea un pcm (raw). Para el caso del mp3 (no se internamente exactamente que hace) pero se que miente, osea, el mp3 es el jpeg del audio, necesitas traducirlo nuevamente a datos reales. No se en realidad que es lo que estás metiendo en el archivo que estás usando, si son datos crudos, datos + metadata, etc. En conclusión, no se si debés decodificar el mp3 antes de poder usarlo o no(si es que la herramienta que estás usando te permite eso, que segun veo de github lo permite pero no se si lo estás usando como se debe)
   


Los datos que obtengo son los mismos datos que se le pasan por audioclip.SetData(float[], int), de echo lo he probado y a través del SetData si puedo escucharlo, pero necesito introducirlo a medida de que el audioclip lo necesita por tema de rendimiento, la clase que uso para leer el fichero es esta https://github.com/WulfMarius/NAudio-Unity/blob/master/NAudio/Wave/WaveStreams/AudioFileReader.cs como puede ver lo que hace es ver la extensión del archivo, y según dicha extensión pues usa un lector u otro. Tampoco entiendo por qué al llamar AudoClip.Create() con el stream en false es cuando se bloquea el juego durante un 1 segundo y sin embargo el SetData no tarda nada.

Etiquetas: