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