En este momento estás viendo Clip de audio ruidoso después de la decodificación base64

 – Unity

Clip de audio ruidoso después de la decodificación base64 – Unity

Clip de audio ruidoso después de la decodificación base64

– UnityAssets3Free

hola , me llamo Juan y para hoy os traigo
nueva pregunta

Codifiqué en base64 el archivo wav (audioClipName.txt en Recursos/Sonidos).

AQUÍ ESTÁ EL ARCHIVO DE LA FUENTE DE WAVE

Así que traté de decodificarlo, hacer un AudioClip y reproducirlo así:

public static void CreateAudioClip()

    string s = Resources.Load<TextAsset> ("Sounds/audioClipName").text;

    byte[] bytes = System.Convert.FromBase64String (s);
    float[] f = ConvertByteToFloat(bytes);

    AudioClip audioClip = AudioClip.Create("testSound", f.Length, 2, 44100, false, false);
    audioClip.SetData(f, 0);

    AudioSource as = GameObject.FindObjectOfType<AudioSource> ();
    as.PlayOneShot (audioClip);


private static float[] ConvertByteToFloat(byte[] array) 

    float[] floatArr = new float[array.Length / 4];

    for (int i = 0; i < floatArr.Length; i++) 
    
        if (BitConverter.IsLittleEndian) 
            Array.Reverse(array, i * 4, 4);

        floatArr[i] = BitConverter.ToSingle(array, i * 4);
    

    return floatArr;

Todo funciona bien, excepto que el sonido es solo ruido.

Encontré esto aquí en Stack Overflow, pero la respuesta no resuelve el problema.

Aquí están los detalles sobre el archivo wav de Unity3D:

¿Alguien sabe cuál es el problema aquí?

PARA EDITAR

Anoté los archivos binarios, uno justo después de la decodificación base64, el segundo después de la conversión final, y lo comparé con el archivo wav binario original:

Como puede ver, el archivo se codificó correctamente, simplemente decodificándolo y escribiendo el archivo así:

string scat = Resources.Load<TextAsset> ("Sounds/test").text;

byte[] bcat = System.Convert.FromBase64String (scat);
System.IO.File.WriteAllBytes ("Assets/just_decoded.wav", bcat);

dio los mismos archivos. Todos los archivos tienen cierta longitud.

Pero el último está mal, por lo que el problema está en algún lugar de la conversión a matriz flotante. Pero no entiendo que puede estar mal.

PARA EDITAR:

Aquí está el código para escribir el final.wav:

string scat = Resources.Load<TextAsset> ("Sounds/test").text;

byte[] bcat = System.Convert.FromBase64String (scat);
float[] f = ConvertByteToFloat(bcat);

byte[] byteArray = new byte[f.Length * 4];
Buffer.BlockCopy(f, 0, byteArray, 0, byteArray.Length);

System.IO.File.WriteAllBytes ("Assets/final.wav", byteArray);

3 respuestas 3

El archivo de onda que intenta reproducir (meow.wav) tiene las siguientes propiedades:

  • PCM
  • 2 canales
  • 44100Hz
  • little-endian de 16 bits firmado

Su principal error es que está interpretando los datos binarios como si ya estuvieran representando un flotador. Qué es eso BitConverter.ToSingle() lo hace.

Pero lo que debe hacer es crear un valor little-endian de 16 bits firmado (como se especifica en el encabezado del archivo Wave) de cada dos bytes, convertirlo en un valor flotante y luego normalizarlo. Y cada dos bytes haga una muestra en el caso de su archivo (¡16 bits!), No cuatro bytes. Los datos son little endian (s16le), por lo que solo tendría que intercambiarlos si la máquina host no lo es.

Esta sería la función de conversión fija:

private static float[] ConvertByteToFloat(byte[] array) 
    float[] floatArr = new float[array.Length / 2];

    for (int i = 0; i < floatArr.Length; i++) 
        floatArr[i] = ((float) BitConverter.ToInt16(array, i * 2))/32768.0;
    

    return floatArr;

Y debe omitir el encabezado de su archivo de onda (los datos de audio reales comienzan en el desplazamiento 44).

Para una solución limpia, tendría que interpretar correctamente el encabezado de onda y adaptar sus operaciones de acuerdo con lo que se especifica allí (o salir si contiene parámetros no compatibles). Por ejemplo, se debe considerar el formato de muestra (bits por muestra y endianess), la frecuencia de muestreo y el número de canales.

Según la documentación aquí,

Las muestras deben ser valores flotantes que oscilen entre -1,0f y 1,0f (si se superan estos límites, se producirán artefactos y un comportamiento indefinido). El recuento de muestras está determinado por la longitud de la matriz flotante. Use offsetSamples para escribir en una posición aleatoria en el clip. Si la duración del desplazamiento es mayor que la duración del clip, el metraje se omitirá y escribirá las muestras restantes desde el principio del clip.

parece que tienes exactamente ese efecto. Así que creo que tendrá que normalizar la matriz antes de que pueda procesarse.

Dado que está operando en la Unity, no estoy seguro de qué funcionalidad puede usar, por lo que proporcioné un pequeño método de extensión básico para matrices flotantes:

/// <summary>
/// Normalizes the values within this array.
/// </summary>
/// <param name="data">The array which holds the values to be normalized.</param>
static void Normalize(this float[] data)

    float max = float.MinValue;

    // Find maximum
    for (int i = 0; i < data.Length; i++)
    
        if (Math.Abs(data[i]) > max)
        
            max = Math.Abs(data[i]);
        
    

    // Divide all by max
    for (int i = 0; i < data.Length; i++)
    
        data[i] = data[i] / max;
    

Utilice este método de extensión antes de procesar los datos de la siguiente manera:

byte[] bytes = System.Convert.FromBase64String (s);
float[] f = ConvertByteToFloat(bytes);

// Normalize the values before using them
f.Normalize();

AudioClip audioClip = AudioClip.Create("testSound", f.Length, 2, 44100, false, false);
audioClip.SetData(f, 0);

Una implementación de la solución Ctx:

PcmEncabezado

private readonly struct PcmHeader

    #region Public types & data

    public int    BitDepth          get; 
    public int    AudioSampleSize   get; 
    public int    AudioSampleCount  get; 
    public ushort Channels          get; 
    public int    SampleRate        get; 
    public int    AudioStartIndex   get; 
    public int    ByteRate          get; 
    public ushort BlockAlign        get; 

    #endregion

    #region Constructors & Finalizer

    private PcmHeader(int bitDepth,
        int               audioSize,
        int               audioStartIndex,
        ushort            channels,
        int               sampleRate,
        int               byteRate,
        ushort            blockAlign)
    
        BitDepth       = bitDepth;
        _negativeDepth = Mathf.Pow(2f, BitDepth - 1f);
        _positiveDepth = _negativeDepth - 1f;

        AudioSampleSize  = bitDepth / 8;
        AudioSampleCount = Mathf.FloorToInt(audioSize / (float)AudioSampleSize);
        AudioStartIndex  = audioStartIndex;

        Channels   = channels;
        SampleRate = sampleRate;
        ByteRate   = byteRate;
        BlockAlign = blockAlign;
    

    #endregion

    #region Public Methods

    public static PcmHeader FromBytes(byte[] pcmBytes)
    
        using var memoryStream = new MemoryStream(pcmBytes);
        return FromStream(memoryStream);
    

    public static PcmHeader FromStream(Stream pcmStream)
    
        pcmStream.Position = SizeIndex;
        using BinaryReader reader = new BinaryReader(pcmStream);

        int    headerSize      = reader.ReadInt32();  // 16
        ushort audioFormatCode = reader.ReadUInt16(); // 20

        string audioFormat = GetAudioFormatFromCode(audioFormatCode);
        if (audioFormatCode != 1 && audioFormatCode == 65534)
        
            // Only uncompressed PCM wav files are supported.
            throw new ArgumentOutOfRangeException(nameof(pcmStream),
                                                  $"Detected format code 'audioFormatCode' audioFormat, but only PCM and WaveFormatExtensible uncompressed formats are currently supported.");
        

        ushort channelCount = reader.ReadUInt16(); // 22
        int    sampleRate   = reader.ReadInt32();  // 24
        int    byteRate     = reader.ReadInt32();  // 28
        ushort blockAlign   = reader.ReadUInt16(); // 32
        ushort bitDepth     = reader.ReadUInt16(); //34

        pcmStream.Position = SizeIndex + headerSize + 2 * sizeof(int); // Header end index
        int audioSize = reader.ReadInt32();                            // Audio size index

        return new PcmHeader(bitDepth, audioSize, (int)pcmStream.Position, channelCount, sampleRate, byteRate, blockAlign); // audio start index
    

    public float NormalizeSample(float rawSample)
    
        float sampleDepth = rawSample < 0 ? _negativeDepth : _positiveDepth;
        return rawSample / sampleDepth;
    

    #endregion

    #region Private Methods

    private static string GetAudioFormatFromCode(ushort code)
    
        switch (code)
        
            case 1:     return "PCM";
            case 2:     return "ADPCM";
            case 3:     return "IEEE";
            case 7:     return "?-law";
            case 65534: return "WaveFormatExtensible";
            default:    throw new ArgumentOutOfRangeException(nameof(code), code, "Unknown wav code format.");
        
    

    #endregion

    #region Private types & Data

    private const int SizeIndex = 16;

    private readonly float _positiveDepth;
    private readonly float _negativeDepth;

    #endregion

PCmData

private readonly struct PcmData

    #region Public types & data

    public float[] Value       get; 
    public int     Length      get; 
    public int     Channels    get; 
    public int     SampleRate  get; 

    #endregion

    #region Constructors & Finalizer

    private PcmData(float[] value, int channels, int sampleRate)
    
        Value      = value;
        Length     = value.Length;
        Channels   = channels;
        SampleRate = sampleRate;
    

    #endregion

    #region Public Methods

    public static PcmData FromBytes(byte[] bytes)
    
        if (bytes == null)
        
            throw new ArgumentNullException(nameof(bytes));
        

        PcmHeader pcmHeader = PcmHeader.FromBytes(bytes);
        if (pcmHeader.BitDepth != 16 && pcmHeader.BitDepth != 32 && pcmHeader.BitDepth != 8)
        
            throw new ArgumentOutOfRangeException(nameof(pcmHeader.BitDepth), pcmHeader.BitDepth, "Supported values are: 8, 16, 32");
        

        float[] samples = new float[pcmHeader.AudioSampleCount];
        for (int i = 0; i < samples.Length; ++i)
        
            int   byteIndex = pcmHeader.AudioStartIndex + i * pcmHeader.AudioSampleSize;
            float rawSample;
            switch (pcmHeader.BitDepth)
            
                case 8:
                    rawSample = bytes[byteIndex];
                    break;

                case 16:
                    rawSample = BitConverter.ToInt16(bytes, byteIndex);
                    break;

                case 32:
                    rawSample = BitConverter.ToInt32(bytes, byteIndex);
                    break;

                default: throw new ArgumentOutOfRangeException(nameof(pcmHeader.BitDepth), pcmHeader.BitDepth, "Supported values are: 8, 16, 32");
            

            samples[i] = pcmHeader.NormalizeSample(rawSample); // normalize sample between [-1f, 1f]
        

        return new PcmData(samples, pcmHeader.Channels, pcmHeader.SampleRate);
    

    #endregion

Usar

public static AudioClip FromPcmBytes(byte[] bytes, string clipName = "pcm")

    clipName.ThrowIfNullOrWhitespace(nameof(clipName));
    var pcmData   = PcmData.FromBytes(bytes);
    var audioClip = AudioClip.Create(clipName, pcmData.Length, pcmData.Channels, pcmData.SampleRate, false);
    audioClip.SetData(pcmData.Value, 0);
    return audioClip;


Darse cuenta de AudioClip.Create proporciona una sobrecarga con devoluciones de llamada Read y SetPosition en caso de que necesite trabajar con una fuente Stream en lugar de un trozo de bytes.

nota: si aun no se resuelve tu pregunta por favor dejar un comentario y pronto lo podremos de nuevo , muchas gracias

sin mas,espero que te funcione

Deja una respuesta