none
C# Wave-Datei dekodieren RRS feed

  • Frage

  • Hallo Forum,

    ich versuche zur Zeit eine Wave-Datei mit einer Konsolenanwendung zu entschlüsseln um dann im Anschluss daran selbst Wave-Dateien schreiben zu können.
    Dabei habe ich dieses Schema hier gefunden:

    https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

    Zu diesem Schema habe ich ein provisorisches C# Programm geschrieben um mich ein bisschen mit dem Thema zu beschäftigen:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    
    namespace Wave_Reader
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Datei öffnen
                FileStream File = new FileStream(@"C:\Users\Daniel\Desktop\batR.wav", FileMode.Open);
    
                // Größe der Datei bestimmen
                long Size = File.Length;
    
                // Daten der Chunks
                byte[] ChunkID = new byte[4];
                byte[] ChunkSize = new byte[4];
                byte[] Format = new byte[4];
                byte[] SubChunkID = new byte[4];
                byte[] SubChunkSize = new byte[4];
                byte[] AudioFormat = new byte[2];
                byte[] Channel = new byte[2];
                byte[] Samplerate = new byte[4];
                byte[] Byterate = new byte[4];
                byte[] BlockAlign = new byte[2];
                byte[] BitsPerSample = new byte[2];
                byte[] SubChunk2ID = new byte[4];
                byte[] SubChunk2Size = new byte[4];
    
                // Dekoder erstellen
                ASCIIEncoding Encoder = new ASCIIEncoding();
    
                // Mehrdimensionales Array für die Chunks
                byte[][] Chunk = new byte[3][];
                Chunk[0]= new byte[12];
                Chunk[1] = new byte[24];
                Chunk[2] = new byte[Size - 36];
    
                // Ersten Chunk auslesen
                File.Read(Chunk[0], 0, 12);
    
                // Zweiten Chunk auslesen
                File.Read(Chunk[1], 0, 24);
    
                // Dritten Chunk auslesen
                for (int i = 0; i <= (Chunk[2].Length - 1); i++)
                {
                    Chunk[2][i] = (byte)File.ReadByte();
                }
    
                // Datei freigeben
                File.Close();
    
                // Daten aus dem Array holen
                ChunkID = Chunk[0].Skip(0).Take(4).ToArray();
                ChunkSize = Chunk[0].Skip(4).Take(4).ToArray();
                Format = Chunk[0].Skip(8).Take(4).ToArray();
                SubChunkID = Chunk[1].Skip(0).Take(4).ToArray();
                SubChunkSize = Chunk[1].Skip(4).Take(4).ToArray();
                AudioFormat = Chunk[1].Skip(8).Take(2).ToArray();
                Channel = Chunk[1].Skip(10).Take(2).ToArray();
                Samplerate = Chunk[1].Skip(12).Take(4).ToArray();
                Byterate = Chunk[1].Skip(16).Take(4).ToArray();
                BlockAlign = Chunk[1].Skip(20).Take(2).ToArray();
                BitsPerSample = Chunk[1].Skip(22).Take(2).ToArray();
                SubChunk2ID = Chunk[2].Skip(2).Take(4).ToArray();
                SubChunkSize = Chunk[2].Skip(6).Take(4).ToArray();
    
                // Konsolenausgabe
                Console.WriteLine("Chunk 1");
                Console.WriteLine("Chunk ID: " + Encoder.GetString(ChunkID));
                Console.WriteLine("Größe der Wave Datei: " + (BitConverter.ToInt32(ChunkSize, 0) + 8) + " Bytes");
                Console.WriteLine("Format: " + Encoder.GetString(Format));
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("Chunk 2");
                Console.WriteLine("Chunk ID: " + Encoder.GetString(SubChunkID));
                Console.WriteLine("Chunk Size: " + SubChunkSize[3] + SubChunkSize[2] + SubChunkSize[1] + SubChunkSize[0]);
                Console.WriteLine("Audio Format: " + BitConverter.ToInt16(AudioFormat, 0));
                Console.WriteLine("Anzahl Kanäle: " + BitConverter.ToInt16(Channel, 0));
                Console.WriteLine("Samplerate: " + BitConverter.ToInt32(Samplerate, 0) + " Hz");
                Console.WriteLine("Byterate: " + BitConverter.ToInt32(Byterate, 0));
                Console.WriteLine("Bytes pro Sample: " + BitConverter.ToInt16(BlockAlign, 0));
                Console.WriteLine("Bits pro Sample: " + BitConverter.ToInt16(BitsPerSample, 0));
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("Chunk 3");
                Console.WriteLine("Chunk ID: " + Encoder.GetString(SubChunk2ID));
                Console.WriteLine("Chunk Size: " + BitConverter.ToInt32(SubChunk2Size, 0));
    
                Console.ReadLine();
            }
        }
    }
    

    Allerdings bekomme ich als Konsolenausgabe folgendes:

    Die rot markierten Stellen verwirren mich ein bisschen und ich weiß nicht wo der Fehler sein könnte.
    Im obersten Kreis soll (laut der Internetseite) eine 16 stehen und im unteren das Wort "data".

    Wo habe ich mich vertan bzw. wo ist mein Fehler?
    Danke für die Hilfe!

    Gruß
    Daniel

    Montag, 4. November 2013 19:37

Antworten

  • Hallo,
    als erstes musst du hier das Skip weg lassen:
    SubChunk2ID = Chunk[2]/*.Skip(2)*/.Take(4).ToArray();
    In meinem Test lag "data" direkt am Anfang des Blocks. Weiterhin weißt du zweimal SubChunkSize zu, was zum 1. Fehler Führt. Lösche die letzte Zeile des Zuweisungsblocks, dann kommt 16 heraus:
    //SubChunkSize = Chunk[2].Skip(6).Take(4).ToArray();
    Kleiner Tipp noch am Ende, packe den Stream in einen using-Block. Dann wird die Datei auch geschlossen wenn ein Fehler auftritt (und das ohne finally).


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Als Antwort vorgeschlagen Ciprian Bogdan Dienstag, 5. November 2013 07:18
    • Als Antwort markiert Kampino89 Dienstag, 5. November 2013 21:43
    Montag, 4. November 2013 21:17
    Moderator

Alle Antworten

  • Hallo,
    als erstes musst du hier das Skip weg lassen:
    SubChunk2ID = Chunk[2]/*.Skip(2)*/.Take(4).ToArray();
    In meinem Test lag "data" direkt am Anfang des Blocks. Weiterhin weißt du zweimal SubChunkSize zu, was zum 1. Fehler Führt. Lösche die letzte Zeile des Zuweisungsblocks, dann kommt 16 heraus:
    //SubChunkSize = Chunk[2].Skip(6).Take(4).ToArray();
    Kleiner Tipp noch am Ende, packe den Stream in einen using-Block. Dann wird die Datei auch geschlossen wenn ein Fehler auftritt (und das ohne finally).


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Als Antwort vorgeschlagen Ciprian Bogdan Dienstag, 5. November 2013 07:18
    • Als Antwort markiert Kampino89 Dienstag, 5. November 2013 21:43
    Montag, 4. November 2013 21:17
    Moderator
  • Wunderbar!
    Dank dir vielmals. Nun klappt es wie es soll. Bei dem doppelten SubChunkSize habe ich wohl einfach den Wald vor lauter Bäumen nicht gesehen....ärgerlich.

    Und das Skip muss weggelassen werden weil der erst skippt und dann die vier Bytes nimmt zum umwandeln?
    Dann könnte ich ja auch in der ersten Zeile das Skip(0) weglassen.

    Der Tipp mit dem using ist richtig gut. Habe bisher immer mit open und close gearbeitet....lief auch recht gut aber war halt immer ärgerlich wenn du das close vergessen hast :)

    Montag, 4. November 2013 21:35
  • Und das Skip muss weggelassen werden weil der erst skippt und dann die vier Bytes nimmt zum umwandeln?
    Dann könnte ich ja auch in der ersten Zeile das Skip(0) weglassen.

    Genau. Ich habe einfach die 72 Bytes von der Webseite in eine Datei gespeichert und dann mal den Debugger beim Zuweisen angehalten und die Werte gesucht. Sie standen bei den Indizes 0, 1, 2 und 3. Da in diesem Fall Skip(0) verwendet werden müsste (0 Bytes überspringen) kann man es auch gleich weg lassen. Und stimmt, das gilt für jedes Skip(0) ;)

    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Montag, 4. November 2013 21:48
    Moderator
  • Gut gut.....
    Jetzt wo du es sagst....mit dem Debugger sollte ich mich auch mal beschäftigen.. :)
    Der Vollständigkeit halber hier noch mal der fertige Code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    
    namespace Wave_Reader
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Datei öffnen
                FileStream File = new FileStream(@"C:\Users\Daniel\Desktop\batR.wav", FileMode.Open);
    
                // Größe der Datei bestimmen
                long Size = File.Length;
    
                // Daten der Chunks
                byte[] ChunkID = new byte[4];
                byte[] ChunkSize = new byte[4];
                byte[] Format = new byte[4];
                byte[] SubChunkID = new byte[4];
                byte[] SubChunkSize = new byte[4];
                byte[] AudioFormat = new byte[2];
                byte[] Channel = new byte[2];
                byte[] Samplerate = new byte[4];
                byte[] Byterate = new byte[4];
                byte[] BlockAlign = new byte[2];
                byte[] BitsPerSample = new byte[2];
                byte[] SubChunk2ID = new byte[4];
                byte[] SubChunk2Size = new byte[4];
    
                // Dekoder erstellen
                ASCIIEncoding Encoder = new ASCIIEncoding();
    
                // Mehrdimensionales Array für die Chunks
                byte[][] Chunk = new byte[3][];
                Chunk[0]= new byte[12];
                Chunk[1] = new byte[24];
                Chunk[2] = new byte[Size - 36];
    
                // Ersten Chunk auslesen
                File.Read(Chunk[0], 0, 12);
    
                // Zweiten Chunk auslesen
                File.Read(Chunk[1], 0, 24);
    
                // Dritten Chunk auslesen
                for (int i = 0; i <= (Chunk[2].Length - 1); i++)
                {
                    Chunk[2][i] = (byte)File.ReadByte();
                }
    
                // Datei freigeben
                File.Close();
    
                // Daten aus dem Array holen
                ChunkID = Chunk[0].Take(4).ToArray();
                ChunkSize = Chunk[0].Skip(4).Take(4).ToArray();
                Format = Chunk[0].Skip(8).Take(4).ToArray();
                SubChunkID = Chunk[1].Skip(0).Take(4).ToArray();
                SubChunkSize = Chunk[1].Skip(4).Take(4).ToArray();
                AudioFormat = Chunk[1].Skip(8).Take(2).ToArray();
                Channel = Chunk[1].Skip(10).Take(2).ToArray();
                Samplerate = Chunk[1].Skip(12).Take(4).ToArray();
                Byterate = Chunk[1].Skip(16).Take(4).ToArray();
                BlockAlign = Chunk[1].Skip(20).Take(2).ToArray();
                BitsPerSample = Chunk[1].Skip(22).Take(2).ToArray();
                SubChunk2ID = Chunk[2].Take(4).ToArray();
                SubChunk2Size = Chunk[2].Skip(4).Take(4).ToArray();
    
                // Konsolenausgabe
                Console.WriteLine("Chunk 1");
                Console.WriteLine("Chunk ID: " + Encoder.GetString(ChunkID));
                Console.WriteLine("Größe der Wave Datei: " + (BitConverter.ToInt32(ChunkSize, 0) + 8) + " Bytes");
                Console.WriteLine("Format: " + Encoder.GetString(Format));
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("Chunk 2");
                Console.WriteLine("Chunk ID: " + Encoder.GetString(SubChunkID));
                Console.WriteLine("Chunk Size: " + BitConverter.ToInt16(SubChunkSize, 0));
                Console.WriteLine("Audio Format: " + BitConverter.ToInt16(AudioFormat, 0));
                Console.WriteLine("Anzahl Kanäle: " + BitConverter.ToInt16(Channel, 0));
                Console.WriteLine("Samplerate: " + BitConverter.ToInt32(Samplerate, 0) + " Hz");
                Console.WriteLine("Byterate: " + BitConverter.ToInt32(Byterate, 0));
                Console.WriteLine("Bytes pro Sample: " + BitConverter.ToInt16(BlockAlign, 0));
                Console.WriteLine("Bits pro Sample: " + BitConverter.ToInt16(BitsPerSample, 0));
                Console.WriteLine("-----------------------------------------");
                Console.WriteLine("Chunk 3");
                Console.WriteLine("Chunk ID: " + Encoder.GetString(SubChunk2ID));
                Console.WriteLine("Chunk Size: " + BitConverter.ToInt32(SubChunk2Size, 0));
    
                Console.ReadLine();
            }
        }
    }

    Falls jemand mal einen lauffähigen Ansatz haben möchte. Und ansonsten kann das Thema geschlossen werden :)
    Hat sich alles geklärt.

    Danke nochmals!

    Dienstag, 5. November 2013 21:42