visual studio sound reproducir play net c# .net audio wav

c# - studio - Leyendo un solo canal desde un archivo wav multicanal



sound en c# (1)

Microsoft ha creado un standard que cubre hasta 18 canales. De acuerdo con ellos, el archivo wav debe tener un subgrupo meta especial (en la sección "Formato extensible") que especifica una "máscara de canal" ( dwChannelMask ). Este campo tiene una longitud de 4 bytes (una uint ) que contiene los bits correspondientes de cada canal que está presente, por lo que indica cuál de los 18 canales se usa dentro del archivo.

El diseño del canal maestro

A continuación se muestra el MCL, es decir, el orden en el que los canales existentes deben intercalarse, junto con el valor de bit para cada canal. Si un canal no está presente, el siguiente canal que se encuentre allí se "desplegará" en el lugar del canal faltante y se usará su número de orden, pero nunca el valor del bit. (Los valores de bit son únicos para cada canal, independientemente de la existencia del canal),

Order | Bit | Channel 1. 0x1 Front Left 2. 0x2 Front Right 3. 0x4 Front Center 4. 0x8 Low Frequency (LFE) 5. 0x10 Back Left (Surround Back Left) 6. 0x20 Back Right (Surround Back Right) 7. 0x40 Front Left of Center 8. 0x80 Front Right of Center 9. 0x100 Back Center 10. 0x200 Side Left (Surround Left) 11. 0x400 Side Right (Surround Right) 12. 0x800 Top Center 13. 0x1000 Top Front Left 14. 0x2000 Top Front Center 15. 0x4000 Top Front Right 16. 0x8000 Top Back Left 17. 0x10000 Top Back Center 18. 0x20000 Top Back Right

Por ejemplo, si la máscara de canal es 0x63F (1599), esto indicaría que el archivo contiene 8 canales (FL, FR, FC, LFE, BL, BR, SL y SR).

Leyendo y comprobando la máscara del canal.

Para obtener la máscara, deberá leer los bytes 40, 41, 42 y 43 (suponiendo un índice base de 0, y está leyendo un encabezado wav estándar). Por ejemplo,

var bytes = new byte[50]; using (var stream = new FileStream("filepath...", FileMode.Open)) { stream.Read(bytes, 0, 50); } var speakerMask = BitConverter.ToUInt32(new[] { bytes[40], bytes[41], bytes[42], bytes[43] }, 0);

Luego, debe verificar si el canal deseado existe realmente. Para hacer esto, sugiero crear una enum (definida con [Flags] ) que contenga todos los canales (y sus respectivos valores).

[Flags] public enum Channels : uint { FrontLeft = 0x1, FrontRight = 0x2, FrontCenter = 0x4, Lfe = 0x8, BackLeft = 0x10, BackRight = 0x20, FrontLeftOfCenter = 0x40, FrontRightOfCenter = 0x80, BackCenter = 0x100, SideLeft = 0x200, SideRight = 0x400, TopCenter = 0x800, TopFrontLeft = 0x1000, TopFrontCenter = 0x2000, TopFrontRight = 0x4000, TopBackLeft = 0x8000, TopBackCenter = 0x10000, TopBackRight = 0x20000 }

Y, finalmente, check si el canal está presente.

¿Qué pasa si la máscara del canal no existe?

¡Crea uno tú mismo! En función del número de canales del archivo, tendrá que adivinar qué canales se están usando o simplemente seguir ciegamente el MCL. En el siguiente fragmento de código estamos haciendo un poco de ambos,

public static uint GetSpeakerMask(int channelCount) { // Assume setup of: FL, FR, FC, LFE, BL, BR, SL & SR. Otherwise MCL will use: FL, FR, FC, LFE, BL, BR, FLoC & FRoC. if (channelCount == 8) { return 0x63F; } // Otherwise follow MCL. uint mask = 0; var channels = Enum.GetValues(typeof(Channels)).Cast<uint>().ToArray(); for (var i = 0; i < channelCount; i++) { mask += channels[i]; } return mask; }

Extracción de las muestras.

Para leer muestras de un canal en particular, haces exactamente lo mismo que si el archivo fuera estéreo, es decir, incrementas el contador del bucle en tamaño de fotograma (en bytes).

frameSize = (bitDepth / 8) * channelCount

También es necesario compensar el índice de inicio de su bucle. Aquí es donde las cosas se vuelven más complicadas, ya que tiene que comenzar a leer los datos del número de pedido del canal en función de los canales existentes , la profundidad de los bytes.

¿Qué quiero decir con "basado en canales existentes"? Bueno, debe reasignar el número de orden de los canales existentes desde 1, incrementando el orden para cada canal que esté presente. Por ejemplo, la máscara de canal 0x63F indica que se 0x63F los canales FL, FR, FC, LFE, BL, BR, SL y SR, por lo que los nuevos números de orden de canal para los canales respectivos se verían así (tenga en cuenta que los valores de bits son no y nunca debe ser cambiado ),

Order | Bit | Channel 1. 0x1 Front Left 2. 0x2 Front Right 3. 0x4 Front Center 4. 0x8 Low Frequency (LFE) 5. 0x10 Back Left (Surround Back Left) 6. 0x20 Back Right (Surround Back Right) 7. 0x200 Side Left (Surround Left) 8. 0x400 Side Right (Surround Right)

Notará que faltan FLoC, FRoC y BC, por lo tanto, los canales SL y SR "caen" en los siguientes números de orden más bajos disponibles, en lugar de usar el orden predeterminado de SL & SR (10, 11).

Resumiendo

Entonces, para leer los bytes de un solo canal necesitarías hacer algo similar a esto,

// This code will only return the bytes of a particular channel. It''s up to you to convert the bytes to actual samples. public static byte[] GetChannelBytes(byte[] audioBytes, uint speakerMask, Channels channelToRead, int bitDepth, uint sampleStartIndex, uint sampleEndIndex) { var channels = FindExistingChannels(speakerMask); var ch = GetChannelNumber(channelToRead, channels); var byteDepth = bitDepth / 8; var chOffset = ch * byteDepth; var frameBytes = byteDepth * channels.Length; var startByteIncIndex = sampleStartIndex * byteDepth * channels.Length; var endByteIncIndex = sampleEndIndex * byteDepth * channels.Length; var outputBytesCount = endByteIncIndex - startByteIncIndex; var outputBytes = new byte[outputBytesCount / channels.Length]; var i = 0; startByteIncIndex += chOffset; for (var j = startByteIncIndex; j < endByteIncIndex; j += frameBytes) { for (var k = j; k < j + byteDepth; k++) { outputBytes[i] = audioBytes[(k - startByteIncIndex) + chOffset]; i++; } } return outputBytes; } private static Channels[] FindExistingChannels(uint speakerMask) { var foundChannels = new List<Channels>(); foreach (var ch in Enum.GetValues(typeof(Channels))) { if ((speakerMask & (uint)ch) == (uint)ch) { foundChannels.Add((Channels)ch); } } return foundChannels.ToArray(); } private static int GetChannelNumber(Channels input, Channels[] existingChannels) { for (var i = 0; i < existingChannels.Length; i++) { if (existingChannels[i] == input) { return i; } } return -1; }

Necesito extraer las muestras de un solo canal de un archivo wav que contendrá hasta 12 canales (formato 11.1). Sé que dentro de un archivo estéreo normal, las muestras están intercaladas, primero a la izquierda y luego a la derecha, como así,

[1st L] [1st R] [2nd L] [2nd R]...

Entonces, para leer el canal de la izquierda, haría esto.

for (var i = 0; i < myByteArray.Length; i += (bitDepth / 8) * 2) { // Get bytes and convert to actual samples. }

Y para obtener el canal correcto que haría simplemente for (var i = (bitDepth / 8)...

Pero, ¿qué orden se usa para archivos con más de 2 canales?