[gelöst] wav-Datei Header einlesen

Du bist neu in der Welt von C++? Dann schau hier herein!
Antworten
RogerWilco
Beiträge: 61
Registriert: 26. November 2009 00:08
Kontaktdaten:

[gelöst] wav-Datei Header einlesen

Beitrag von RogerWilco »

Hallo zusammen,

ich vesuche den Header einer wav-Datei einzulesen.
Mein Ansatz sieht folgendermaßen aus:

Code: Alles auswählen

   QFile file("/home/axel/test/DING.WAV");

    file.open(QIODevice::ReadOnly);

    QTextStream in(&file);

    char chunkId[4];
    quint32 chunkSize;
    char riffType[4];

    in >> chunkId >> chunkSize ;//>> riffType;

    QMessageBox box;
    box.setText(QString::number(chunkSize));
    box.exec();
So wie ich das verstehe, fängt jedes wave-File mit einem 4 Byte Text "RIFF" an, gefolgt von 4 Byte für die Dateigröße und dann 4 Byte mit dem Inhalt "WAVE".
Gibt es vielleicht eine Art binaryreader wie bei c#?

chunkSize hat offenbar fälchlischerweise den Wert 0 und wenn ich >>riffType nicht auskommentiere, stürzt das Programm ab.
Etwas bei den Datentypen schein verschoben zu sein...
Zuletzt geändert von RogerWilco am 29. Mai 2012 22:34, insgesamt 4-mal geändert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: wav-Datei Header einlesen

Beitrag von Christian81 »

Eine Wave-Datei ist keine Textdatei - das kann nicht gehen.
Du musst du die Daten schon korrekt mittels QIODevice::read() lesen und auswerten
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
RogerWilco
Beiträge: 61
Registriert: 26. November 2009 00:08
Kontaktdaten:

Re: wav-Datei Header einlesen

Beitrag von RogerWilco »

Ich habe es vorher mit QDataStream probiert. Das hatte ich mir in einem Qt-Buch abgeschaut. Das funktionierte aber gar nicht. Mehr Fehlermeldungen als Codezeilen. Vermutlich im Zusammenhang mit dem >> Operator.
RogerWilco
Beiträge: 61
Registriert: 26. November 2009 00:08
Kontaktdaten:

Re: wav-Datei Header einlesen

Beitrag von RogerWilco »

Danke für den Hinweis. Es hat sich weiter entwickelt zu:

Code: Alles auswählen

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QFile file("C:/Documents and Settings/awilbert/Desktop/DING.WAV");

        file.open(QIODevice::ReadOnly);

        QDataStream in(&file);

        char chunkId[4];
        char chunkSize[4];
        char riffType[4];

        in.readRawData(chunkId, 4);
        in.readRawData(chunkSize, 4);
        in.readRawData(riffType, 4);


        ui->lblChunkId->setText(chunkId);
        ui->lblChunkSize->setText(chunkSize);
        ui->lblRiffId->setText(riffType);

}
Nun müssen noch die Typen umgewandelt werden.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: wav-Datei Header einlesen

Beitrag von franzf »

Umwandeln musst du da nix.

Code: Alles auswählen

#include <QFile>
#include <QDataStream>
#include <QDebug>

struct Header {
    char id[4];
    quint32 len;
    char type[4];
};

char const* file_name = "/tmp/11k16bitpcm.wav";

int main() {
    QFile f(file_name);
    f.open(QIODevice::ReadOnly);
    QDataStream s(&f);
    
    Header h;
    s.readRawData(&h.id[0], 4);
    s >> h.len;                           <---
    s.readRawData(&h.type[0], 4);
    
    qDebug() << h.id << h.len << h.type;
}
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: wav-Datei Header einlesen

Beitrag von Christian81 »

QDataStream hat da gar nichts zu suchen!
"The QDataStream class provides serialization of binary data to a QIODevice". Und da wav sicher nicht von Qt serialisierte Daten enthält...
Wie man die Daten korrekt liest habe ich schon geschrieben...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
RogerWilco
Beiträge: 61
Registriert: 26. November 2009 00:08
Kontaktdaten:

Re: wav-Datei Header einlesen

Beitrag von RogerWilco »

Es auch mit dem QDataStream funktioniert. Allerdings mit einer Einschränkung:
Die QStrings, die ich aus den eingelesenen chars erzeugt habe, waren teilweise länger als die 4 Zeichen.
Vielleicht liegt das an der Tatsache, dass im char die Terminierung fehlt?
Jedenfalls liess sich das PRoblem einfach mit .resize(4) lösen.
Nicht elegant, aber es funktioniert...

Auf jeden Fall schonmal vielen Dank für Eure Tipps!
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Re: [gelöst] wav-Datei Header einlesen

Beitrag von RHBaum »

Vielleicht liegt das an der Tatsache, dass im char die Terminierung fehlt?
definitiv

von an QDataStream solltest vielleicht doch eher die FInger lassen ...
es funktioniert zwar auch zum einlesen mit doubles, floats, ints aller sorten und groessen, ohne das QT da eigene Verwaltungsdaten schreibt, aber das muss so nicht bleiben, und das matchen der QT ints auf die c++ ints muss 100% funktionieren, sonst gibts datenmuell ^^
Den Vorteil den von QDatastream erhaelst, wird dir wahrscheinlich nichts nutzen, eher vielleicht schaden anrichten : es wandelt in eine Plattformunabhaengige Bytefolge, so das auf unterschidlichen plattformen die Dateien lesen kannst.

Besser wie Christian81 vorschlägt, selber binaer lesen ...
Und wo doch so schoen deinen Haeder Binaer sauber spezifiziert hasst !

besser und weniger unstaendlich:

Code: Alles auswählen


struct Header
 {
    char id[4];
    quint32 len;
    char type[4];
};

    QFile f(file_name);
    f.open(QIODevice::ReadOnly);
    Header head; 
    /// nun das lesen, aber in einem Block 
    qint64 iread = f.read(reinterpret_cast<char *>(&head,sizeof(Header )));
    /// nen check
    if(iread == sizeof(Header))
    {
          /// TODO ... 
    }
Wobei die Logic ned ganz stimmt. RIffs koennen mehrere Chunks haben ... du baust was, was nur einen Chaunk kann ....

das einzige was noch ueber bleibt, ist wie deine "char id[4]" und "char type[4]" ordnungsgemaes interpretierst ....
DIe dinger sind wirklich immer 4 Buchstaben ohne /0 terminator.
Der performanteste weg waere die als 4 byte ints zu behandeln ... also statt char[4] gleich quint32 zu nehmen.
DU musst eh die bekannten typen irgendwo definieren als komstanten ... da kannst die auch gleich in ints verwandeln ...
"RIFF" würde z.b. zu "R" + "I" + "F" + "F" -> 0x52 + 0x49 + 0x46 + 0x46 -> wir haben little endian bei wav, also Byte reihenfolge anpassen -> 0x46464952
also wenn nun (ID == 0x46464952 ) wahr ergibt, ist deine erster chunk der Main chunk von ner wave datei ...

Alternativ hat QSTring nen Konstruktor "QString::QString( const QByteArray & ba )" und QByteArray kannst wiederum so konstruieren QByteArray::QByteArray ( const char * data, int size )
und schon bekommst aus deinen 4 fixen char buchstaben ne utf16 basierende 0 terminierte Zeichenkette.
wobei das IMHO unnötig unperformant ist, die strings wirst ja ned staendig brauchen auser fuer debug zwecke ^^

Ciao ...
RogerWilco
Beiträge: 61
Registriert: 26. November 2009 00:08
Kontaktdaten:

Re: [gelöst] wav-Datei Header einlesen

Beitrag von RogerWilco »

Erstmal vielen Dank für die Hilfe.
Ich habe versucht RHBaums Vorschlag in meiner Klasse umzusetzen.

Auszug aus dem Header:

Code: Alles auswählen

class WaveFile
{



public:
    WaveFile();
    WaveFile(QString);


    QString waveFileName;


    // wave file contents:
    QString getChunkId();
    int getChunkSize();
    QString getRiffType();
    QString getHeaderSignature();
    int getFmtHeaderLength();
    int getFormatTag();
    int getNumberOfChannels();
    int getSampleRate();
    int getBytesPerSecond();
    int getBlockAlign();
    int getBitsPerSample();



private:
    struct Header {
        char chunkId[4];
        quint32 chunkSize;
        char riffType[4];
        char headerSignature[4]; // contains blank at end
        quint32 fmtHeaderLength;
        quint16 formatTag;
        quint16 numberOfChannels;
        quint32 sampleRate;
        quint32 bytesPerSecond;
        quint16 blockAlign;
        quint16 bitsPerSample;
    };

};

Code: Alles auswählen

#include "wavefile.h"

WaveFile::WaveFile()
{
}


WaveFile::WaveFile(QString fileName) {

    QFile file(fileName);

    file.open(QIODevice::ReadOnly);
    Header head;                                                               
    qint64 iread = file.read(reinterpret_cast<char *>(&head,sizeof(Header )));

}
Fehlermeldungen:

Code: Alles auswählen

/../Wave4Nvh/wavefile.cpp:14: Fehler:invalid conversion from ‘char*’ to ‘qint64 {aka long long int}’ [-fpermissive]
/usr/include/QtCore/qiodevice.h:118: error:   initializing argument 1 of ‘QByteArray QIODevice::read(qint64)’ [-fpermissive]
/usr/include/QtCore/qbytearray.h:365: Fehler:‘QByteArray::operator QNoImplicitBoolCast() const’ is private

/home/axel/Dokumente/programmierung/c++ qt/Wave4Nvh/Wave4Nvh-build-desktop-Qt_4_7_4_in_Pfad__System__Release/../Wave4Nvh/wavefile.cpp:14: Fehler:within this context

Die Fehlermeldung interpretiere ich als Fehler, der dadurch entsteht, dass ich einen cha* nach qint64 konvertiere.
Aber im "Struct" kommt kein qint64 vor und das qint64 von read() ist der Rückgabetyp für einen eventuellen Fehler.
Oder gibt "sizeof(Header)" etwa keinen qint64 zurück und muss noch umgewandelt werden?
RogerWilco
Beiträge: 61
Registriert: 26. November 2009 00:08
Kontaktdaten:

Re: wav-Datei Header einlesen

Beitrag von RogerWilco »

:oops: :oops: :oops: :oops:
Ohje!

Code: Alles auswählen

qint64 iread = file.read(reinterpret_cast<char *>(&head,sizeof(Header )));
Man beachte die Klammern!

Code: Alles auswählen

...(&head,sizeof(Header )))
Es muß natürlich heissen:
...(&head),sizeof(Header ))
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Re: wav-Datei Header einlesen

Beitrag von RHBaum »

Ohje :-)
Ich koennt ja jetzt sagen ich hab da was eingebaut um zu sehen das alle noch mitdenken ^^
Aber glaub das hilft mir nu ned mehr ^^

@RogerWilco
Ja sorry hab mich bei den Klammern verhaspelt :-)
Aber mit der nötigen Übung wirds Dir bald aehnlich ergehen ... Du schreibst was DU meinst und überlegst an den Stellen nimmer allzuviel, weils eigentlich sonnenklar ist.
Wichtig fuer dich ist, DU musst versuchen zu verstehen was wir da treiben. Das ist alles kein Hexenwerk.
Aber mit der Zeit wird das schon ....

Nur noch ne Frage:
Warum verwendest du QT an der Stelle ?
besser, weil generischer waere es vielleicht ohne QT Typen zu arbeiten .... (std::string statt QString, std::ifstream statt QFile)
QT hat eben nicht nur Vorteile ... So schoen wie die QT auch manchmal ist, lernen und zu starker Focus auf ein Framework ist auch nicht das gelbe ....

Ciao ....
RogerWilco
Beiträge: 61
Registriert: 26. November 2009 00:08
Kontaktdaten:

Re: wav-Datei Header einlesen

Beitrag von RogerWilco »

Na, ich denke, man darf hier schon erwarten, dass Konsumenten wie ich nicht einfach nur kopieren, sondern auch nachdenken...
Leider hat mich die Fehlermeldung in die falsche Richtung gelenkt; aber davon lernt man schließlich...

Qt ist das Standard-Framework auf der Arbeit. Ich möchte das Programm später vielleicht auch dort benutzen können. Vielleicht kann es irgendwann mal wave Dateien in csv konvertieren, damit auch Programme, die mit mehr als 2 Kanälen in Wavedateien nicht klar kommen, zumindest die csv verarbeiten können.
Außerdem möchte ich folendes verstehen: http://www.dgaqs.de/forum2010/prf/TB-12_Lingnau.pdf
Aber das alleine rechtfertigt nicht Qt. Ich habe 2 Bücher als Nachschlagewerke. Die dienen mir als Vorlage (das von Summerfield und das von Wolf).

Ich muß noch vielen lernen und mir fehlt die Übung. Es ist halt nur ein Hobby und wenn man einmal 4 Wochen nichts dran tut, hat man (fast) alles wieder vergessen. Zumindest als Amateur und Anfänger.
Umso dankbarer bin ich für die geduldige und freundliche Unterstützung hier.
Antworten