Viewing hex bytes of a wav file

May 17, 2012 at 1:57am
Would anyone know of a quick and painless way to read in the hex bytes of a wav file into a C++ program? My main goal is to find patterns in a 16 byte row (as it would be displayed in a hex editor) and extract certain bytes to do calculations with. All I need is a way to read in 16 bytes of the wav file at a time.
May 17, 2012 at 2:02am
Just open it in binary mode and read into arrays of two character blocks.
May 17, 2012 at 2:57am
I am curious to know what sort of patterns you think you'll find in only 16 samples.

(or rather, 16 bytes, which may only be 8 samples if 16-bit, or even 4 samples if 16-bit stereo)
May 17, 2012 at 12:16pm
Here's something I wrote a little while ago. It only really extracts one channel so it won't work for everything but it worked for what I needed a while ago:

Here's a class that represents a wav file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class wavFile
{
public:
	//Members
	unsigned char	s1[5];	// "RIFF"
	unsigned long	size;
	unsigned char	s2[5];	// "WAVE"
	unsigned char	s3[5];	// "fmt "
	unsigned long	FormatLength;
	unsigned short	FormatTag;
	unsigned short	nChannels;
	unsigned long	nSamplesPerSec;
	unsigned long	nAvgBytesPerSec;
	unsigned short	nBlockAlign;
	unsigned short	BitsPerSample;
	unsigned char	s4[5];	// "data"
	unsigned long	data_size;
	std::vector<int>		iData;
};



and here's a function that parses a wav file into the class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
int LoadWavFile(string fname, wavFile &Buffer)
{
	// Clear the buffer in case there was something loaded before
	Buffer.iData.clear(); 

	cout << "Opening file" << endl;

	std::ifstream fin(fname, std::ifstream::in | std::ifstream::binary);

	cout << "File open!" << endl;

	fin.read( (char*) Buffer.s1             , 4*sizeof(Buffer.s1[0])	    );	Buffer.s1[4] = '\0';		cout << "\ts1: \t\t\t" << Buffer.s1	<< endl;
	fin.read( (char*)&Buffer.size			, sizeof(Buffer.size)           );	cout << "\tsize: \t\t\t"		 << Buffer.size					<< endl;
	fin.read( (char*) Buffer.s2             , 4*sizeof(Buffer.s2[0])	    );	Buffer.s2[4] = '\0';		cout << "\ts2: \t\t\t" << Buffer.s2	<< endl;
	fin.read( (char*) Buffer.s3             , 4*sizeof(Buffer.s3[0])	    );	Buffer.s3[4] = '\0';		cout << "\ts3: \t\t\t" << Buffer.s3	<< endl;
	fin.read( (char*)&Buffer.FormatLength   , sizeof(Buffer.FormatLength)   );	cout << "\tFormat length: \t\t"	 << Buffer.FormatLength			<< endl;
	fin.read( (char*)&Buffer.FormatTag      , sizeof(Buffer.FormatTag)	    );	cout << "\tFormatTag: \t\t"		 << Buffer.FormatTag			<< endl;
	fin.read( (char*)&Buffer.nChannels      , sizeof(Buffer.nChannels)      );	cout << "\tnChannels: \t\t"		 << Buffer.nChannels			<< endl;  
	fin.read( (char*)&Buffer.nSamplesPerSec , sizeof(Buffer.nSamplesPerSec) );	cout << "\tnSamplesPerSec: \t"	 << Buffer.nSamplesPerSec		<< endl; 
	fin.read( (char*)&Buffer.nAvgBytesPerSec, sizeof(Buffer.nAvgBytesPerSec)); 	cout << "\tnAvgBytesPerSec: \t"	 << Buffer.nAvgBytesPerSec		<< endl;
	fin.read( (char*)&Buffer.nBlockAlign    , sizeof(Buffer.nBlockAlign)    );	cout << "\tnBlockAlign: \t\t"	 << Buffer.nBlockAlign			<< endl;
	fin.read( (char*)&Buffer.BitsPerSample  , sizeof(Buffer.BitsPerSample)  );	cout << "\tBitsPerSample: \t\t"	 << Buffer.BitsPerSample		<< endl;
	fin.read( (char*) Buffer.s4             , 4*sizeof(Buffer.s4[0])	    );	Buffer.s4[4] = '\0';		cout << "\ts4: \t\t\t" << Buffer.s4	<< endl;
	fin.read( (char*)&Buffer.data_size      , sizeof(Buffer.data_size)      );  cout << "\tdata_size: \t\t"		 << Buffer.data_size			<< endl;

	if (Buffer.s1[0] != 'R' || Buffer.s1[1] != 'I' || Buffer.s1[2] != 'F' || Buffer.s1[3] != 'F')
	{
		cout << "Error: Unknown file type is loaded." << endl;
		return 1;
	}
	if (Buffer.s2[0] != 'W' || Buffer.s2[1] != 'A' || Buffer.s2[2] != 'V' || Buffer.s2[3] != 'E')
	{
		cout << "Error: This audio file loaded is not a WAVE file." << endl;
		return 1;
	}
	if (Buffer.FormatTag != 1)
	{
		cout << "Error: This wav file must be in a PCM (8bit, 16bit or 32bit) format." << endl;
		return 1;
	}
	if (Buffer.FormatLength != 16)
	{
		cout << "Error: This wav file includes an extendable format that is not supported." << endl;
		return 1;
	}
	if (Buffer.nChannels != 1)
	{
		cout << "Warning: multiple channels were found in this file.  I am only going to use the first channel" << endl;
	}

	switch (Buffer.BitsPerSample)
	{
		case 8:
		{
			char temp;
			unsigned long nb_samples = Buffer.data_size / sizeof(temp);
			unsigned long Chnl_counter = 0;
			cout << "\tNumber of samples: \t" << nb_samples << endl;

			try 
			{				
				Buffer.iData.reserve(nb_samples);
			}
			catch(...)
			{
				cout << "Error: The file is too big to put into memory" << endl
					 << "\tTry reducing the bitrate" << endl 
					 << "\tTry converting this to mono" << endl
					 << "\tTry closing other applications and/or rebooting to defrag memory" << endl;
				return 1;
			}

			for (unsigned long i=0; i<nb_samples; i++)
			{
				if (Chnl_counter++%Buffer.nChannels == 0) //We only take the first channel
				{
					fin.read((char*)&temp, sizeof(temp));
					Buffer.iData.push_back(temp);
					if (Chnl_counter==Buffer.nChannels) Chnl_counter = 0;
				}
			}
		}
		break;
		case 16:
		{
			short temp;
			unsigned long nb_samples = Buffer.data_size / sizeof(temp);
			unsigned long Chnl_counter = 0;
			cout << "\tNumber of samples: \t" << nb_samples << endl;

			try 
			{				
				Buffer.iData.reserve(nb_samples);
			}
			catch(...)
			{
				cout << "Error: The file is too big to put into memory" << endl
					 << "\tTry reducing the bitrate" << endl 
					 << "\tTry converting this to mono" << endl
					 << "\tTry closing other applications and/or rebooting to defrag memory" << endl;
				return 1;
			}

			for (unsigned long i=0; i<nb_samples; i++)
			{
				if (Chnl_counter++%Buffer.nChannels == 0) //We only take the first channel
				{
					fin.read((char*)&temp, sizeof(temp));
					Buffer.iData.push_back(temp);
					if (Chnl_counter==Buffer.nChannels) Chnl_counter = 0;
				}
			}
		}
		break;
		case 32:
		{
			int temp;
			unsigned long nb_samples = Buffer.data_size / sizeof(temp);
			unsigned long Chnl_counter = 0;
			cout << "\tNumber of samples: \t" << nb_samples << endl;

			try 
			{				
				Buffer.iData.reserve(nb_samples);
			}
			catch(...)
			{
				cout << "Error: The file is too big to put into memory" << endl
					 << "\tTry reducing the bitrate" << endl 
					 << "\tTry converting this to mono" << endl
					 << "\tTry closing other applications and/or rebooting to defrag memory" << endl;
				return 1;
			}

			for (unsigned long i=0; i<nb_samples; i++)
			{
				if (Chnl_counter++%Buffer.nChannels == 0) //We only take the first channel
				{
					fin.read((char*)&temp, sizeof(temp));
					Buffer.iData.push_back(temp);
					if (Chnl_counter==Buffer.nChannels) Chnl_counter = 0;
				}
			}
		}
		break;
		default: 
		{
			cout << "ERROR: unconventional bits per sample.  The wav file is invalid." << endl;
			return 1;
		}
	}

	cout << "\tSamples loaded: \t" << Buffer.iData.size() << endl;
	cout << "File read complete" << endl << endl;

	//Modify the header for only 1 channel:
	Buffer.nChannels		= 1;
	Buffer.nBlockAlign		= Buffer.BitsPerSample	/ 8;
	Buffer.nAvgBytesPerSec	= Buffer.nBlockAlign	* Buffer.nChannels * Buffer.nSamplesPerSec;
	Buffer.data_size		= Buffer.iData.size()	* Buffer.nBlockAlign;
	Buffer.size				= Buffer.data_size		+ 36;

	fin.close();
	return 0;
}
May 17, 2012 at 1:16pm
If the members of your class are defined in the right order, couldn't you just do:

1
2
3
4
5
6
7
8
9
Buffer.iData.clear(); 

	cout << "Opening file" << endl;

	std::ifstream fin(fname, std::ifstream::in | std::ifstream::binary);

	cout << "File open!" << endl;

	fin.read( (char*) &Buffer, sizeof(wavFile));
May 17, 2012 at 1:17pm
Oh well actually you'd need to create two classes, one for the header, and one for the data. The header class would be the same as the one you posted before but without the iData member.
May 17, 2012 at 2:41pm
> find patterns in a 16 byte row (as it would be displayed in a hex editor) ...
> All I need is a way to read in 16 bytes of the wav file at a time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>

struct sedecimet
{
    typedef unsigned char byte ;
    enum { N = 16 } ;
    byte data[N] ;

    sedecimet() : data{0} {} ;

    /* // uncomment if required
    template< typename T > sedecimet( std::initializer_list<T> il )
        : sedecimet( il.begin(), il.end() ) {}

    template< typename ITERATOR > sedecimet( ITERATOR begin, ITERATOR end ) : data{0}
    {
        int cnt = 0 ;
        for( ; begin != end && cnt < N ; ++begin, ++cnt ) data[cnt] = *begin ;
    }
    */

    template< typename C, typename T >
    std::basic_istream<C,T>& read_from( std::basic_istream<C,T>& stm )
    {
        int cnt = 0 ;
        for( auto& b : data ) { b = 0 ; if( stm >> b ) ++cnt ; }
        if(cnt) stm.clear() ;
        return stm ;
    }

    template< typename C, typename T > friend inline
    std::basic_ostream<C,T>& operator<< ( std::basic_ostream<C,T>& stm, const sedecimet& d )
    {
        auto flags = stm.flags() ;
        for( sedecimet::byte b : d.data )
             stm << std::hex << std::setw(2) << std::setfill('0') << int(b) << ' ' ;
        stm.flags(flags) ;
        return stm ;
    }
};

int main()
{
    std::ifstream file( __FILE__ , std::ios::binary ) ;
    sedecimet s ;
    std::ostringstream sstm ;
    while( s.read_from(file) ) sstm << s << '\n' ;
    std::string bytes_as_displayed_in_a_hex_editor = sstm.str() ;

    std::cout << bytes_as_displayed_in_a_hex_editor ;
}

Topic archived. No new replies allowed.