Wave-File auslesen
-
schaue auf Zeile 8 und diesen Beitrag von Michael
schaue auf Zeile 23, 28, 33, usw, und auf die Bemerkung von MFK
-
cpp_freak schrieb:
schaue auf Zeile 8 und diesen Beitrag von Michael
schaue auf Zeile 23, 28, 33, usw, und auf die Bemerkung von MFK
Ok das mit 23, 28, 33 ist mir klar, aber trotzdem müssten ja die anderen Werte dann korrekt sein, die beeinflussen sich ja nicht. Wenn die stimmen wollte ich da weiter machen. Zeile 8 schaue ich mir jetzt mal an.
-
Das kann man ja gar nicht mit ansehen!
Du liest aus einer Datei mit binärem Inhalt doch nur Zeichenketten und uint23_t und uint16_t-Werte. Schreibe Dir doch zunächst mal dafür jeweils eine kleine Funktion, die das erledigt.Es gibt sicher schöneres, als das was ich Dir hier vorschlage - siehe ibinstream, aber ich will das mal auf Deinem Programm aufbauen.
Also mit den drei Funktionen ...
#include <string> #include <istream> #include <cstdint> void readbin( std::istream& in, std::string& str, std::size_t n ) { const std::size_t N = 100; char buf[N]; // hier mal ganz anfängermäßig! if( n > N ) in.setstate( std::ios_base::failbit ); if( in.read( buf, n ) ) str.assign( buf, buf + n ); } void readbin( std::istream& in, std::uint32_t& x ) { // ACHTUNG: Dein OS muss Little Endian haben (z.B.: Windows), nur dann ist dies ok! in.read( (char*)&x, sizeof(x) ); } void readbin( std::istream& in, std::uint16_t& x ) { in.read( (char*)&x, sizeof(x) ); }.. geht alles viel einfacher.
Hier in Deine Methode eingebaut, unter Berücksichtigung dessen, was hier bereits erwähnt wurde.
void WaveFormat::readFileVersuchen (Steinberg::String samplePath) { using namespace std; ifstream fileStream(samplePath.c_str(), ios_base::binary ); // ios_base::binary MUSS sein if (!fileStream.is_open()) return; string tag; readbin( fileStream, tag, 4 ); if( !fileStream || tag != "RIFF" ) // mChunkID { cerr << "keine RIFF-Datei" << endl; return; } readbin( fileStream, mChunkSize ); readbin( fileStream, tag, 4 ); if( !fileStream || tag != "WAVE" ) // mFormat { cerr << "keine WAVE-Datei" << endl; return; } for( uint32_t len; ; ) { // -- Header des jeweiligen Chunks einlesen: tag + len readbin( fileStream, tag, 4 ); readbin( fileStream, len ); if( !fileStream ) break; // EOF cout << "Chunk: " << tag << " Len:" << len << endl; if( tag == "fmt " ) // abhängig vom Chunk-Typ die Details lesen { readbin( fileStream, mAudioFormat ); readbin( fileStream, mNumChannels ); readbin( fileStream, mSampleRate ); readbin( fileStream, mByteRate ); readbin( fileStream, mBlockAlign ); readbin( fileStream, mBitsPerSample ); if( fileStream ) cout << "fmt ohne Fehler gelesen" << endl; } // else if( tag == "data" ) // hier kommt der Ton // {} else { fileStream.ignore( len ); // unbekannten Chunk überspringen } } }und schon sind es nur noch 50 Zeilen statt 90 und die Methode tut auch noch mehr.
Gruß
Werner
-
Werner Salomon schrieb:
Das kann man ja gar nicht mit ansehen!
Du liest aus einer Datei mit binärem Inhalt doch nur Zeichenketten und uint23_t und uint16_t-Werte. Schreibe Dir doch zunächst mal dafür jeweils eine kleine Funktion, die das erledigt.Es gibt sicher schöneres, als das was ich Dir hier vorschlage - siehe ibinstream, aber ich will das mal auf Deinem Programm aufbauen.
Also mit den drei Funktionen ...
#include <string> #include <istream> #include <cstdint> void readbin( std::istream& in, std::string& str, std::size_t n ) { const std::size_t N = 100; char buf[N]; // hier mal ganz anfängermäßig! if( n > N ) in.setstate( std::ios_base::failbit ); if( in.read( buf, n ) ) str.assign( buf, buf + n ); } void readbin( std::istream& in, std::uint32_t& x ) { // ACHTUNG: Dein OS muss Little Endian haben (z.B.: Windows), nur dann ist dies ok! in.read( (char*)&x, sizeof(x) ); } void readbin( std::istream& in, std::uint16_t& x ) { in.read( (char*)&x, sizeof(x) ); }.. geht alles viel einfacher.
Hier in Deine Methode eingebaut, unter Berücksichtigung dessen, was hier bereits erwähnt wurde.
void WaveFormat::readFileVersuchen (Steinberg::String samplePath) { using namespace std; ifstream fileStream(samplePath.c_str(), ios_base::binary ); // ios_base::binary MUSS sein if (!fileStream.is_open()) return; string tag; readbin( fileStream, tag, 4 ); if( !fileStream || tag != "RIFF" ) // mChunkID { cerr << "keine RIFF-Datei" << endl; return; } readbin( fileStream, mChunkSize ); readbin( fileStream, tag, 4 ); if( !fileStream || tag != "WAVE" ) // mFormat { cerr << "keine WAVE-Datei" << endl; return; } for( uint32_t len; ; ) { // -- Header des jeweiligen Chunks einlesen: tag + len readbin( fileStream, tag, 4 ); readbin( fileStream, len ); if( !fileStream ) break; // EOF cout << "Chunk: " << tag << " Len:" << len << endl; if( tag == "fmt " ) // abhängig vom Chunk-Typ die Details lesen { readbin( fileStream, mAudioFormat ); readbin( fileStream, mNumChannels ); readbin( fileStream, mSampleRate ); readbin( fileStream, mByteRate ); readbin( fileStream, mBlockAlign ); readbin( fileStream, mBitsPerSample ); if( fileStream ) cout << "fmt ohne Fehler gelesen" << endl; } // else if( tag == "data" ) // hier kommt der Ton // {} else { fileStream.ignore( len ); // unbekannten Chunk überspringen } } }und schon sind es nur noch 50 Zeilen statt 90 und die Methode tut auch noch mehr.
Gruß
Wernervielen dank, aber habe trotzdem nochmal weiter versucht selber das hinzukiregen.
Hat auch endlich halbwegs geklappt! Ich kriege jetzt die richtigen Werte heraus, allerdings nur in hexadezimal, und manche muss man davon noch umdrehen. Bei der Samplerate steht zum Beispiel: 0080bb00. Dreht man das um steht dort bb80 und das sind 48000! Und das ist ja bekanntlich nen passender Wert für ne Samplerate. Ich denkee das liegt dann wohl an little und Big Endian. Werde jetzt mal versuchen das komplett hinzukriegen.
Code:void WaveFormat::readFileVersuchen (Steinberg::String samplePath) { bool dump = true; wofstream ftest("C:\\Users\\Otto\\waveDaten.txt", ios::out); std::string fmtMarker; std::string fmtString = "fmt "; int chunkState = kChunkDescriptor; fstream fileStream; fileStream.open(samplePath.text (), ios::in); int byteCounter = 0; bool isSignalSounded = false; // Gibt true an, wenn fmt erreicht wurde int8 oneByte = 0; if (fileStream.is_open()) { while (!fileStream.eof()) { //int au = sizeof(Steinberg::uint8); fileStream.read(&oneByte, sizeof(Steinberg::uint8)); if (dump) { ftest << oneByte << std::hex; } if (chunkState == kChunkDescriptor) { if (byteCounter>=0 && byteCounter <= 3) { mChunkID <<= sizeof(Steinberg::uint8) * 8; mChunkID |= oneByte & 0xFF; } else if (byteCounter>=4 && byteCounter <=7) { mChunkSize <<= sizeof(Steinberg::uint8) * 8; mChunkSize |= oneByte & 0xFF; } else if (byteCounter>=8 && byteCounter <=11) { mFormat <<= sizeof(Steinberg::uint8) * 8; mFormat |= oneByte & 0xFF; if (byteCounter == 11) { chunkState = kFmtSubChunk; } } } else if (chunkState == kFmtSubChunk) { static const string kFmtMarker = ""; if (oneByte == 0) { fmtMarker.push_back (' '); } else { fmtMarker.push_back (oneByte); int hmmmm = fmtMarker.find(fmtString); if (hmmmm > 0 && !isSignalSounded) // && !isSignalSounded entfernen um die Sachen nach fmt in die Datei mitzuschreiben { fileStream.read(&oneByte, sizeof(Steinberg::uint8)); // ??? isSignalSounded = true; // Da fmt erreicht wurde, wird hier nicht mehr reingesprungen byteCounter = 17; // Setze den ByteCounter auf 17 zurück, damit die Schleifen unterhalb durchlaufen werden. ofstream ftest("C:\\Users\\Otto\\waveDaten.txt", ios::out); // Schreibt zum Testen eine ftest << fmtMarker; // Textdatei mit den Daten. (Noch keine umgewandelten) //turnHex(0xccbb3344); // Test ob das umdrehen klappt } } /* if (byteCounter>=12 && byteCounter <=15 && isSignalSounded == true) { mSubChunk1ID <<= sizeof(Steinberg::uint8) * 8; mSubChunk1ID |= oneByte & 0xFF; } */ if (byteCounter>=16 && byteCounter <=19 && isSignalSounded == true) { mSubChunk1Size <<= sizeof(Steinberg::uint8) * 8; mSubChunk1Size |= oneByte & 0xFF; } else if (byteCounter>=20 && byteCounter <=21 && isSignalSounded == true) { mAudioFormat <<= sizeof(Steinberg::uint8) * 8; mAudioFormat |= oneByte & 0xFF; } else if (byteCounter>=22 && byteCounter <=23 && isSignalSounded == true) { mNumChannels <<= sizeof(Steinberg::uint8) * 8; mNumChannels |= oneByte & 0xFF; } else if (byteCounter>=24 && byteCounter <=27 && isSignalSounded == true) { mSampleRate <<= sizeof(Steinberg::uint8) * 8; mSampleRate |= oneByte & 0xFF; } else if (byteCounter>=28 && byteCounter <=31 && isSignalSounded == true) { mByteRate <<= sizeof(Steinberg::uint8) * 8; mByteRate |= oneByte & 0xFF; } else if (byteCounter>=32 && byteCounter <=33 && isSignalSounded == true) { mBlockAlign <<= sizeof(Steinberg::uint8) * 8; mBlockAlign |= oneByte & 0xFF; } else if (byteCounter>=34 && byteCounter <=35 && isSignalSounded == true) { mBitsPerSample <<= sizeof(Steinberg::uint8) * 8; mBitsPerSample |= oneByte & 0xFF; } else if (byteCounter>=36 && byteCounter <=39 && isSignalSounded == true) { mSubChunks2ID <<= sizeof(Steinberg::uint8) * 8; mSubChunks2ID |= oneByte & 0xFF; } else if (byteCounter>=40 && byteCounter <=43 && isSignalSounded == true) { mSubchunck2Size <<= sizeof(Steinberg::uint8) * 8; mSubchunck2Size |= oneByte & 0xFF; } else if (byteCounter >=44) { } } byteCounter++; } } }
-
AsterixderGallier schrieb:
vielen dank, aber habe trotzdem nochmal weiter versucht selber das hinzukiregen.
Hat auch endlich halbwegs geklappt! Ich kriege jetzt die richtigen Werte heraus, allerdings nur in hexadezimal, und manche muss man davon noch umdrehen. Bei der Samplerate steht zum Beispiel: 0080bb00. Dreht man das um steht dort bb80 und das sind 48000! Und das ist ja bekanntlich nen passender Wert für ne Samplerate. Ich denkee das liegt dann wohl an little und Big Endian.Nö - das liegt an Dir.
Ändere an Deinem Programm folgendes:
- Zeile 9 Daten BINÄR lesenfileStream.open(samplePath.text (), ios::in | ios::binary);wenn Du das nicht tust, wird spätestens das Lesen der Audiodaten fehlschlagen!
- Zeile 57 >= 0
if (hmmmm >= 0 && !isSignalSounded)wird der fmt_-String gefunden, so kann '
hmmmm' auch den Wert 0 annehmen, falls der erste Chunk bereits der 'fmt_'-Chunk ist.- Zeile 61
byteCounter = 16;- Zeile 89 und 90
ersetze diese beiden Zeilen bitte durchif( byteCounter == 24 ) mSampleRate = 0; mSampleRate |= (oneByte & 0xFF) << 8*(byteCounter - 24);Du wirst sehen, dass hilft
- auch wenn Du danach noch nicht am Ziel bist.Gruß
Werner
-
Hallo AsterixDerGallier,
kennst du schon das for-case Antipattern? Dein Code ist also ein Paradebeispiel dafür.
Auch wenn der Code von Werner dir vllt. jetzt noch etwas zu hoch vorkommt, so solltest du doch unsere Ratschläge annehmen...
-
Werner Salomon schrieb:
AsterixderGallier schrieb:
vielen dank, aber habe trotzdem nochmal weiter versucht selber das hinzukiregen.
Hat auch endlich halbwegs geklappt! Ich kriege jetzt die richtigen Werte heraus, allerdings nur in hexadezimal, und manche muss man davon noch umdrehen. Bei der Samplerate steht zum Beispiel: 0080bb00. Dreht man das um steht dort bb80 und das sind 48000! Und das ist ja bekanntlich nen passender Wert für ne Samplerate. Ich denkee das liegt dann wohl an little und Big Endian.Nö - das liegt an Dir.
Ändere an Deinem Programm folgendes:
- Zeile 9 Daten BINÄR lesenfileStream.open(samplePath.text (), ios::in | ios::binary);wenn Du das nicht tust, wird spätestens das Lesen der Audiodaten fehlschlagen!
- Zeile 57 >= 0
if (hmmmm >= 0 && !isSignalSounded)wird der fmt_-String gefunden, so kann '
hmmmm' auch den Wert 0 annehmen, falls der erste Chunk bereits der 'fmt_'-Chunk ist.- Zeile 61
byteCounter = 16;- Zeile 89 und 90
ersetze diese beiden Zeilen bitte durchif( byteCounter == 24 ) mSampleRate = 0; mSampleRate |= (oneByte & 0xFF) << 8*(byteCounter - 24);Du wirst sehen, dass hilft
- auch wenn Du danach noch nicht am Ziel bist.Gruß
WernerDanke ich glaube das hilft mir schonmal weiter!! Aber muss erstmal etwas sehen
-
Th69 schrieb:
Hallo AsterixDerGallier,
kennst du schon das for-case Antipattern? Dein Code ist also ein Paradebeispiel dafür.
Auch wenn der Code von Werner dir vllt. jetzt noch etwas zu hoch vorkommt, so solltest du doch unsere Ratschläge annehmen...Ja versuche ich ja, aber ich will halt soweit wie möglich auf den code selber kommen. Wenn er erstmal läuft ist es doch auch einfacher ihn zu verbessern, weil dann kann man immer testen wenn etwas verändert wurde ob es noch läuft.
Das ist eigentlich meine erste (für mich) ziemlich komplexe Sache die ich versuche zu machen. Hab mir vorgenommen ganz am Ende vllt nen Drumcomputer daraus zu machen, aber das ist sicher noch lange hin.
-
So mein Code funktioniert:
void WaveFormat::readFileVersuchen (Steinberg::String samplePath) { std::string fmtMarker; std::string fmtString = "fmt "; int chunkState = kChunkDescriptor; fstream fileStream; fileStream.open(samplePath.text (), ios::in | ios::binary); int byteCounter = 0; bool isSignalSounded = false; // Gibt true an, wenn fmt erreicht wurde int8 oneByte = 0; if (fileStream.is_open()) { while (!fileStream.eof()) { fileStream.read(&oneByte, sizeof(Steinberg::uint8)); if (chunkState == kChunkDescriptor) { if (byteCounter>=0 && byteCounter <= 3) // Liest die ChunkID ein (RIFF) { if (byteCounter == 0) mChunkID = ""; mChunkID.push_back (oneByte); } else if (byteCounter>=4 && byteCounter <=7) // Liest die Größe des Data-Blocks ein (etwas kleiner als die Datei) { if( byteCounter == 4 ) mChunkSize = 0; mChunkSize |= (oneByte & 0xFF) << 8*(byteCounter - 4); } else if (byteCounter>=8 && byteCounter <=11) // Liest das Format ein (WAVE), push_back hängt jeweils oneByte an den String ran { if (byteCounter == 8) mFormat = ""; mFormat.push_back(oneByte); if (byteCounter == 11) { chunkState = kFmtSubChunk; } } } else if (chunkState == kFmtSubChunk) { static const string kFmtMarker = ""; if (oneByte == 0) { fmtMarker.push_back (' '); } else { fmtMarker.push_back (oneByte); int hmmmm = fmtMarker.find(fmtString); if (hmmmm >= 0 && !isSignalSounded) // && !isSignalSounded entfernen um die Sachen nach fmt in die Datei mitzuschreiben { fileStream.read(&oneByte, sizeof(Steinberg::uint8)); // ??? isSignalSounded = true; // Da fmt erreicht wurde, wird hier nicht mehr reingesprungen byteCounter = 16; // Setze den ByteCounter auf 17 zurück, damit die Schleifen unterhalb durchlaufen werden. mSubChunk1ID = "fmt "; ofstream ftest("C:\\Users\\Otto\\waveDaten.txt", ios::out); // Schreibt zum Testen eine ftest << fmtMarker; // Textdatei mit den Daten. (Noch keine umgewandelten) } } if (byteCounter>=16 && byteCounter <=19 && isSignalSounded == true) { if( byteCounter == 22 ) // Ist dafür da, dass auch eine zweite Datei danach eingelesen werden kann, und der alte Wert vorher wieder auf 0 gesetzt wird mSubChunk1Size = 0; mSubChunk1Size |= (oneByte & 0xFF) << 8*(byteCounter - 16); } else if (byteCounter>=20 && byteCounter <=21 && isSignalSounded == true) { if( byteCounter == 20 ) mAudioFormat = 0; mAudioFormat |= (oneByte & 0xFF) << 8*(byteCounter - 20); } else if (byteCounter>=22 && byteCounter <=23 && isSignalSounded == true) { if( byteCounter == 22 ) mNumChannels = 0; mNumChannels |= (oneByte & 0xFF) << 8*(byteCounter - 22); } else if (byteCounter>=24 && byteCounter <=27 && isSignalSounded == true) { if( byteCounter == 24 ) mSampleRate = 0; mSampleRate |= (oneByte & 0xFF) << 8*(byteCounter - 24); } else if (byteCounter>=28 && byteCounter <=31 && isSignalSounded == true) { if( byteCounter == 28 ) mByteRate = 0; mByteRate |= (oneByte & 0xFF) << 8*(byteCounter - 28); } else if (byteCounter>=32 && byteCounter <=33 && isSignalSounded == true) { if( byteCounter == 32 ) mBlockAlign = 0; mBlockAlign |= (oneByte & 0xFF) << 8*(byteCounter - 32); } else if (byteCounter>=34 && byteCounter <=35 && isSignalSounded == true) { if( byteCounter == 34 ) mBitsPerSample = 0; mBitsPerSample |= (oneByte & 0xFF) << 8*(byteCounter - 34); } else if (byteCounter>=36 && byteCounter <=39 && isSignalSounded == true) { if( byteCounter == 36 ) mSubChunks2ID = 0; mSubChunks2ID |= (oneByte & 0xFF) << 8*(byteCounter - 36); } else if (byteCounter>=40 && byteCounter <=43 && isSignalSounded == true) { if( byteCounter == 40 ) mSubchunck2Size = 0; mSubchunck2Size |= (oneByte & 0xFF) << 8*(byteCounter - 40); } else if (byteCounter >=44) { // Data-Block einlesen } } byteCounter++; } } fileStream.close(); }Muss man dieses auf 0 setzen der Werte
if( byteCounter == 20 ) mAudioFormat = 0; mAudioFormat |= (oneByte & 0xFF) << 8*(byteCounter - 20);in jedem Block machen oder besser einfach am Anfang bei byteCounter == 0 alle zurück setzen. Wie sollte ich den Code noch verbessern, bzw. aufräumen? Vielen Dank schonmal bishierhin.
-
Mach doch bitte endlich das for-case Pattern raus! Das wurde dir schon in der allerersten Antwort gesagt! Überhaupt wurden dir schon ganz viele wichtige Fehler genannt, von denen du nicht eine Sache auch nur angeguckt hast. Z.B. das while(!eof) ist immer noch drin. Lies am besten den ganzen Thread nochmal langsam und genau und setz alles um ,was dir genannt wurde.
-
AsterixDerGallier schrieb:
Wie sollte ich den Code noch verbessern, bzw. aufräumen?
so wie's da steht.