Struct, Wave und der Mut des Verzweifelten!



  • Hi,

    also ich sitze jetzt schon länger an einem Problem. Ich möchte prinzipiell aus einer WAV Datei div. gegebene Daten auslesen!
    Dazu habe ich schon hundert tausend Seiten im Internet durchforstet auf "brauchbare Code-Samples", aber ich konnte nicht wirklich etwas finden und habe mich dann selbst hingesetzt.

    Bin, was structs, pointer und File-Arithmetik angeht Anfänger und hangle mich langsam nach vorne (learning by doing 😞 ).

    Ich habe jetzt aber das Problem, dass die IDs nicht korrekt ausgelesen werden, ich aber die Files anhand dieser auf "Valid Wave File" überprüfen möchte!

    Hier erstmal der Code:
    Das ganze ist natürlich nur C, kein C++.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct waveheader
    {
      char riff_name[4];
      long riff_length;
      char wave_name[4];
      char fmt_name[4];
      long fmt_length;
      short fmt_type;
      short channel_number;
      long sampling_per_sec;
      long avgrage_byte_per_sec;
      short sampling_byte;
      short sampling_bit;
      char data_name[4];
      long data_length;
      long data_info[3];
    } wavehead;
    
    unsigned *getInput(char **argv)
    {
      struct waveheader wavehead;
      FILE *wave_pointer;
      wave_pointer = fopen(argv[1], "rb");
    
      fread(&wavehead,sizeof(wavehead), 1, wave_pointer);
    
      printf("RIFF Name: %s\n", wavehead.riff_name);
      printf("Riff Laenge: %d\n", wavehead.riff_length);
      printf("RIFF Type: %s\n", wavehead.wave_name);
      printf("FMT Name: %s\n", wavehead.fmt_name);
      printf("RIFF Laenge: %d\n", wavehead.fmt_length);
      printf("Beats pro Sample: %d\n", wavehead.fmt_type);
      printf("Kanalzahl: %d\n", wavehead.channel_number);
      printf("Samplerate: %d\n", wavehead.sampling_per_sec);
      printf("Samplerate: %d\n", wavehead.sampling_byte);
      printf("Samplerate: %d\n", wavehead.sampling_bit);
      printf("Data: %s\n", wavehead.data_name);
      printf("Data Length: %d\n", wavehead.data_length);
      printf("Samplerate: %s\n", wavehead.data_info);
    
    }
    

    Data und Samplerate bleiben sogar komplett leer.

    Ich weiß echt nicht mehr woran das liegt. Außerdem bekomme ich immer anstatt von WAVE ein "WAVEfmt" (das fmt sollte eigtl. wavehead.fmt_name sein und wavehead.wave_name sollte WAVE sein!).

    Es wäre schonmal ein riesen Schritt wenn wenigstens die Namen der Chunks (fmt,
    data) ordentlich ausgegeben werden würden.

    File Arithmetik WAV: Wikipedia WAV Dateistruktur

    Ich hoffe, dass es sich hierbei um ein einfaches Problem handelt das man schnell lösen kann.

    Vielen Dank gleich mal im Vorhinein!

    MfG cheza

    PS: Solltet ihr noch Infos brauchen, einfach posten!



  • Erstmal könnte es Probleme mit dem Packing geben (der Compiler darf Füllbytes zwischen den struct-Elementen einfügen, um sie auf bestimmte Positionen im Adressraum auszurichten) - du kannst ja mal mit sozeof() und ofsetof() kontrollieren, ob deine Struktur die richtige Größe und Anordnung hat.

    Zweitens: Die Kenn-Strings im WAV-Header sind nicht null-terminiert, also weiß printf() nicht, wo es zu lesen aufhören soll (und das 'fmt ' steht im Speicher direkt hinter dem 'WAVE') - da kannst du als Lösung die Größe explizit angeben: printf("%4s",wavehead.wave_name);

    PS: Die eigentlichen Daten würde ich übrigens nicht in der Header-Struktur unterbringen.



  • Erstmal könnte es Probleme mit dem Packing geben (der Compiler darf Füllbytes zwischen den struct-Elementen einfügen, um sie auf bestimmte Positionen im Adressraum auszurichten) - du kannst ja mal mit sozeof() und ofsetof() kontrollieren, ob deine Struktur die richtige Größe und Anordnung hat.

    Das mit dem Einschieben von Füllbytes dachte ich mir schon. Habe dazu auch ein wenig im Internet gefunden aber ich weiß nicht wie ich das verhindern kann.
    Ich weiß, dass man mittels einem bestimmten Befehl das Auffüllen verhindern kann, jedoch würde ich gerne wissen ob es nicht einen anderen Weg gibt dem Packing zu entkommen!

    Zweitens: Die Kenn-Strings im WAV-Header sind nicht null-terminiert, also weiß printf() nicht, wo es zu lesen aufhören soll (und das 'fmt ' steht im Speicher direkt hinter dem 'WAVE') - da kannst du als Lösung die Größe explizit angeben: printf("%4s",wavehead.wave_name);

    Immer auf die Gefahr hin, dass ich einfach unfähig bin, möchte ich jetzt trotzdem behaupten, dass das nicht klappt! Zumindest bei mir (gcc4.1.1x) nicht!

    Ich hoffe doch, dass sich noch jemand meldet :). Angäblich ist es wirklich "kinderleicht" WAV Header auszulesen, aber mir macht das doch größere Schwierigkeiten als angenommen!

    Und jetzt hätte ich noch eine Grundlagen Frage:

    Wenn ich mit "fopen" eine Datei öffnen und dann mit "fread" diese Datei Byteweise einlese, wird dann bei fread automatisch Speicher allokiert oder muss ich das noch "von Hand" machen?
    Irgendwie habe ich dazu keine Informationen gefunden.

    Ich bedanke mich nochmals, immerhin bin ich doch etwas schwer von Begriff (befürchte ich 🙄 ) aber hier wird einem wirklich toll und nett geholfen!

    MfG cheza



  • Hi,

    Du kannst auch das Parsing "per Hand machen": Statt direkt in ein struct zu lesen, liest Du in einen "anonymen Buffer" und verpointerst darauf:

    Statt

    unsigned *getInput(char **argv)
    {
      struct waveheader wavehead;
      FILE *wave_pointer;
      wave_pointer = fopen(argv[1], "rb");
      fread(&wavehead,sizeof(wavehead), 1, wave_pointer);
    ...
    }
    

    Nimmst Du dann:
    ...

    struct waveheaderPtr
    {
      char* riff_name;
      long* riff_length;
      char* wave_name;
      char* fmt_name;
      long* fmt_length;
      short* fmt_type;
      short* channel_number;
      long* sampling_per_sec;
      long* avgrage_byte_per_sec;
      short* sampling_byte;
      short* sampling_bit;
      char* data_name;
      long* data_length;
      long* data_info;
    };
    
    enum { FIXED_SIZE = ??? }; /* selber zählen */
    
    unsigned *getInput(char **argv)
    {
        struct waveheaderPtr w;
        FILE *wave_pointer;
        char buf[FIXED_SIZE];
        char* pos = buf;
    
        wave_pointer = fopen(argv[1], "rb");
    
        fread(buf, FIXED_SIZE, 1, wave_pointer);
    
    /* manuell zuweisen */
        w.riff_name = pos; pos += 4;
        w.riff_length = (long*) pos; pos += sizeof(long);
        w.wave_name = pos; pos += 4;
        w.fmt_name = pos;  pos += 4;
        ...
    }
    

    Ist nicht schön und hat einige Falltüren, funktioniert aber ... und lässt sich ein wenig "aufhübschen" durch Verwendung einer entsprechenden initWaveHeader-Funktion. Letztlich fallen Dir damit auch Fehler/Mißverständnisse im Format auf (indem Dur direkt im buf nachsiehst und Bytes zählst) und Du kannst verschiedene HeaderFormate unterstützen (indem Du die Pointer entsprechend anders setzt...

    Gruß,

    Simon2.



  • cheza schrieb:

    Erstmal könnte es Probleme mit dem Packing geben (der Compiler darf Füllbytes zwischen den struct-Elementen einfügen, um sie auf bestimmte Positionen im Adressraum auszurichten) - du kannst ja mal mit sozeof() und ofsetof() kontrollieren, ob deine Struktur die richtige Größe und Anordnung hat.

    Das mit dem Einschieben von Füllbytes dachte ich mir schon. Habe dazu auch ein wenig im Internet gefunden aber ich weiß nicht wie ich das verhindern kann.
    Ich weiß, dass man mittels einem bestimmten Befehl das Auffüllen verhindern kann, jedoch würde ich gerne wissen ob es nicht einen anderen Weg gibt dem Packing zu entkommen!

    Die Daten in einen entsprechend großen Speicherblock unstrukturiert einlesen und dann entweder dein struct darauf verzeigern (siehe Simon's Beitrag) oder die Daten elementweise in dein struct reinkopieren.

    Zweitens: Die Kenn-Strings im WAV-Header sind nicht null-terminiert, also weiß printf() nicht, wo es zu lesen aufhören soll (und das 'fmt ' steht im Speicher direkt hinter dem 'WAVE') - da kannst du als Lösung die Größe explizit angeben: printf("%4s",wavehead.wave_name);

    Immer auf die Gefahr hin, dass ich einfach unfähig bin, möchte ich jetzt trotzdem behaupten, dass das nicht klappt! Zumindest bei mir (gcc4.1.1x) nicht!

    Was heißt "funktioniert nicht"?

    Ich hoffe doch, dass sich noch jemand meldet :). Angäblich ist es wirklich "kinderleicht" WAV Header auszulesen, aber mir macht das doch größere Schwierigkeiten als angenommen!

    Sicher ist es leicht - allerdings vermutlich nicht anfängertauglich (sprich: bevor du dich damit beschäftigst, solltest du die Grundlagen von C verstanden haben).

    Wenn ich mit "fopen" eine Datei öffnen und dann mit "fread" diese Datei Byteweise einlese, wird dann bei fread automatisch Speicher allokiert oder muss ich das noch "von Hand" machen?
    Irgendwie habe ich dazu keine Informationen gefunden.

    Alle Funktionen von C, die mit Speicher operieren, erwarten von dir, daß du genügend Platz bereitstellst (entweder als Variable oder per malloc()) - da ist fread() keine Ausnahme.



  • Was heißt "funktioniert nicht"?

    Exakt die selbe Ausgabe wie zuvor. %4s ignoriert einfach die 4 und schreibt wieder z.B. WAVEfmt

    Alle Funktionen von C, die mit Speicher operieren, erwarten von dir, daß du genügend Platz bereitstellst (entweder als Variable oder per malloc()) - da ist fread() keine Ausnahme.

    Da sage ich danke :)! Genau so eine Aussage suchte ich, wurde aber nicht fündig, bis jetzt :)!

    MfG cheza



  • cheza schrieb:

    Was heißt "funktioniert nicht"?

    Exakt die selbe Ausgabe wie zuvor. %4s ignoriert einfach die 4 und schreibt wieder z.B. WAVEfmt

    Sorry, mein Fehler - die width gibt die Mindestgröße der Ausgabe an, die Maximalgröße bestimmst du mit der precision (also printf("%.4s",wave_name); ).


Log in to reply