c# - convertir - Clip de audio ruidoso después de decodificar desde base64
js base64 encoder (2)
Codifiqué el archivo wav en base64 (audioClipName.txt en Recursos / Sonidos).
AQUÍ ESTÁ EL ARCHIVO DE ONDA DE FUENTE
Luego 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 un ruido.
Encontré esto aquí en desbordamiento de pila, pero el dosificador de respuesta resuelve el problema.
Aquí hay detalles sobre el archivo wav de Unity3D:
¿Alguien sabe cuál es el problema aquí?
EDITAR
Escribí archivos binarios, uno justo después de decodificar desde 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 porque acaba de decodificarlo y escribir el archivo de esta manera:
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 algo de longitud.
Pero el último es incorrecto, por lo que el problema está en algún lugar en la conversión a la matriz flotante. Pero no entiendo lo que podría estar mal.
EDITAR:
Aquí está el código para anotar 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);
De acuerdo con la documentación aquí ,
Las muestras deben ser flotantes que van desde -1.0f a 1.0f (exceder estos límites dará lugar a artefactos y 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 longitud del desplazamiento es más larga que la longitud del clip, la escritura se ajustará y escribirá las muestras restantes desde el inicio del clip.
parece que tienes exactamente ese efecto. Así que supongo que tendrás que normalizar la matriz antes de que pueda ser procesada.
Como está trabajando en unidad, no estoy seguro de qué funcionalidad puede usar, así 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;
}
}
Use este método de extensión antes de seguir procesando los datos de esta 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);
El archivo de onda que intenta reproducir ( meow.wav
) tiene las siguientes propiedades:
- PCM
- 2 canales
- 44100 Hz
- firmado little-endian de 16 bits
Su error principal es que está interpretando los datos binarios como si ya estuviera representando un flotante . Esto es lo que hace BitConverter.ToSingle()
.
Pero lo que debe hacer es crear un valor firmado de little bitian de 16 bits (como se especifica en el encabezado Wavefile) de cada dos bytes, convertirlo en un flotante y luego normalizarlo. Y cada dos bytes hacen una muestra en el caso de su archivo (¡16 bits!), No cuatro bytes. Los datos son poco endian (s16le), por lo que solo tendrías que cambiarlos si la máquina host no.
Esta sería la función de conversión corregida:
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 ondas (los datos de audio reales comienzan en el desplazamiento 44).
Para una solución limpia, tendría que interpretar el encabezado de Wave correctamente y adaptar sus operaciones según lo especificado allí (o rescatar si contiene parámetros no compatibles). Por ejemplo, se debe tener en cuenta el formato de muestra (bits por muestra y endianess), frecuencia de muestreo y número de canales.