Bytes to Int Funktion liefert seltsame Werte



  • Hallo,

    derzeit beschäftige ich mich damit Audiodaten aus einer Wavefile zu laden.
    Dazu habe ich mir eine Hilfsfunktion geschrieben, welche aus n Bytes in Little Endian Format einen entsprechenden (zunächst unsigned) Integerwert bastelt.

    Das tue ich wie folgt:

    template <unsigned long _bytes,class RandomAccessIterator>
    unsigned long BytesToInt(const RandomAccessIterator& it)
    {
        unsigned long result(0);
        unsigned long bytes(_bytes);//_bytes is no L-Value therefore a copy is  //needed sadly
    
        while(bytes--)
        {
    
            result += *(it+bytes) << 8*(bytes);//Litte Endian
        }
        return result;
    
    }
    

    Ich habe meine Waverohdaten (also ohne Header) in einem String gespeichert und wollte die Werte nun in 16 bit signed integer umwandeln.
    Mir liefert nun der (sinngemäße) Aufruf:

    std::string somewavedata;
    for(auto it = somewavedata.begin(); it != somewavedata.end(); advance(it,sizeof(short)))
    {
    std::cout << ByteToInt<2>(it) << std::endl;
    }
    

    Ab und An Werte im Bereich ~ 10^16, was aber 8 Byte von nöten machte.
    Da ich aber nur zwei byte aus dem String jeweils auslese frage ich mich, wie es dazu kommen kann, dass ich z.B. den Wert 18446744073709551352 als Rückgabewert der ByteToInt-Funktion erhalte?!
    Ich habe auch mal zur Sicherheit in ByteToInt *it erstmal auf unsigned char gecastet mit dem selben Ergebnis.
    Wie kann das sein?



  • Achso, ich habe das zwar hier inkonsistent gepostet, aber sizeof(short) ist auf meinem System definitiv 2.



  • Wo verwendest du short? Hast du eine 64 Bit Anwendung? Warum tut es ein einfaches memcpy nicht? Warum ueberhaupt Template? Wie liest du den String ein? Warum std::string und nicht std::vector<char>?

    it != somewavedata.end();

    Was wenn du beim Vorruecken um 2 das Ende verfehlst? => UB?

    *(it+bytes)
    

    Wird das zu einem unsigned long promotet bevor geshiftet wird?

    So viele Fragen und so wenig Code.



  • Na dann mal der Reihe nach:

    Template, weil ich es einfach der Übungshalber machen wollte (so simpel es auch sein mag)
    Memcpy finde ich super hässlich.

    Den String übernehme ich als substring aus dem gesamten Wavefile inkl. header.
    Pseudocode:

    std::string file;
    std::ifstream wavefile("mywave.wav",std::ios_base::binary);
    wavefile >> std::noskipws;
    std::copy(...)->file
    myfile.close();
    ReadHeader(file)
    wavedata = file.substring(POSITIONWAVEDATA,EOF);
    

    Ja ich habe eine 64 bit Anwendung.
    std::string habe ich nur wegen der bequemen "find()" Methode verwendet, da dies das Auffinden der Wavefelder "RIFF","fmt " sowie "data" gestattet.
    Beim std::vector<char> hätte ich die erstmal selber schreiben müssen.

    Was wenn du beim Vorruecken um 2 das Ende verfehlst? => UB?

    Das ist richtig und mir auch bewusst.
    Das Laden der Wave ist nur mittel zum zweck(in der späteren Anwendung brauch ich das nicht) um die sound API zu testen d.h. ich brauch nur die rohdaten. (sowie z.B. samplerate, aber das klappt)

    *(it+bytes)
    Wird das zu einem unsigned long promotet bevor geshiftet wird?

    Nein. Ich war mir nicht sicher, was passiert wenn man den Wert aus dem Wertebereich des Variablentyps "rausshiftet" konnte bei Tests aber nur das Intuitive verhalten bestätigen (d.h. es wurde augenscheinlich zu einem Typ größerer Wortbreite gecastet).

    Die Shorts verwende ich anschließend als Daten für die Audiowiedergabe



  • *(it+bytes)
    

    Ist vom Typ char. Wenn 213 drinsteht, dann ist das gleich -23 (oder so). Wird es dann auf long long (64 Bit) erweitert, dann bleibt die negative Zahl erhalten. Castest du dann auf unsigned, dann kommt eine sehr grosse Zahl heraus. * Keine Ahnung, ob shift fuer signed Typen definiert ist. deswegen ja: std::vector<unsigned char> .

    Memcpy finde ich super hässlich.

    Und warum dann nicht std::copy mit (u)char-Pointern? Warum deine selbstgebaute Schleife?

    * Angabe ohne Gewaehr.



  • Hallo,

    danke für deine Hilfe!
    Nach deiner "Rückversicherung", dass es schon sein kann, dass die Werte so groß sind habe ich nochmal weiterprogrammiert und es funktioniert 🙂



  • Binäre Daten in einem std::string zu verwalten ist generell Blödsinn.
    (In speziellen Fällen kann es natürlich Sinn machen, aber das hier ist keiner dieser Fälle.)

    Und die "fmt " und "data" Chunks in einem RIFF File sucht man ganz sicher nicht mit find() , sondern indem man die Chunk-Struktur parst.
    Das RIFF Format ist super-einfach, das hast du in < einer Stunde geschrieben.

    Und dein BytesToInt Template hat Undefined Behavior. Die Konvertierung von negativen Werten in unsigned Typen ist nicht definiert.
    Wenn du es zum Üben machst, dann mach es ordentlich. Oder willst du üben Undefined Behavior zu produzieren?



  • hustbaer schrieb:

    Binäre Daten in einem std::string zu verwalten ist generell Blödsinn.
    (Im speziellen Fällen kann es natürlich Sinn machen, aber das hier ist keiner dieser Fälle.)

    wieso ist das blödsinn?

    Und die "fmt " und "data" Chunks in einem RIFF File sucht man ganz sicher nicht mit find() , sondern indem man die Chunk-Struktur parst.
    Das RIFF Format ist super-einfach, das hast du in < einer Stunde geschrieben.

    Wie würdest du sie denn parsen?

    Und dein BytesToInt Template hat Undefined Behavior. Die Konvertierung von negativen Werten in unsigned Typen ist nicht definiert.
    Wenn du es zum Üben machst, dann mach es ordentlich. Oder willst du üben Undefined Behavior zu produzieren?

    Jo, das habe ich jetzt gefixt. (Wobei es auf dem Testsystem in diesem Fall trotzdem ging :P)



  • Namenloser324 schrieb:

    hustbaer schrieb:

    Binäre Daten in einem std::string zu verwalten ist generell Blödsinn.
    (Im speziellen Fällen kann es natürlich Sinn machen, aber das hier ist keiner dieser Fälle.)

    wieso ist das blödsinn?

    Weil std::string "Text" impliziert.

    Und die "fmt " und "data" Chunks in einem RIFF File sucht man ganz sicher nicht mit find() , sondern indem man die Chunk-Struktur parst.
    Das RIFF Format ist super-einfach, das hast du in < einer Stunde geschrieben.

    Wie würdest du sie denn parsen?

    Ja ganz normal halt... 😕

    Abgesehen von diversen Überprüfungen sieht das ca. so aus:

    ParseRiffFile(range):
        riffSignature = ReadDword(range, 0)
        Assert(riffSignature is 'RIFF')
        riffSize = ReadDword(range, 4)
        ParseListChunkContents(range.SubRange(8, riffSize))
    
    ParseListChunkContents(range):
        listSignature = ReadDword(range, 0)
        EmitListStart(listSignature)
        while (range not empty)
            range = ParseChunk(range)
        EmitListEnd(listSignature)
    
    ParseChunk(range):
        chunkSignature = ReadDword(range, 0)
        chunkSize = ReadDword(range, 4)
        if (chunkSignature is 'RIFF' or chunkSignature is 'LIST')
            ParseListChunkContents(range.SubRange(8, chunkSize))
        else
            chunkData = ReadByteArray(range, 8, chunkSize)
            EmitChunkData(chunkSignature, chunkData)
        evenChunkSize = (chunkSize + 1) & ~1
        return range.SubRange(8 + evenChunkSize, range.Size - 8 - evenChunkSize)
    


  • Ok, danke sehr 🙂


Log in to reply