declaring a vector's type as variable

I am trying to read a wav file so that I can analyze the wave forms. To do this, I am placing each sample into a vector. My issue is that there could be 8, 16 or maybe 32 bits per sample so I am having trouble defining the vector type.

My working method right now uses 3 vectors (lines 20-22), one of each type. The file will only populate one of these vectors depending on the bits per sample.

This means that I have a very ugly switch statement (line 44 to 86) when I write and I'll have equally ugly switch statements every time I want to analyze the data itself.

Is there a way for me to condense these into 1 vector? I'm thinking template<typename> or typedef could be of use, but I'm not sure how to do this.

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
#include <vector>
#include <fstream>
using std::vector; using std::ifstream;

struct wavFile
{
    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;

//    EDIT: This was my original problem
//    vector<char>     cSamp; 
//    vector<short>    sSamp; 
//    vector<int>      iSamp; 
//    This was my solution: (convert everything to 32 bits on the way in)
    vector<int> iSamp;
};


void LoadWavFile(char *fname, wavFile &DATA)
{
    //This was another problem:
    //ifstream fin(fname);
    //Solution:
    ifstream fin(fname, std::ifstream::binary);

    fin.read( (char*) DATA.s1             , 4*sizeof(unsigned char)     ); DATA.s1[4] = '\0';
    fin.read( (char*)&DATA.size           , sizeof(DATA.size)           );
    fin.read( (char*) DATA.s2             , 4*sizeof(unsigned char)     ); DATA.s2[4] = '\0';
    fin.read( (char*) DATA.s3             , 4*sizeof(unsigned char)     ); DATA.s3[4] = '\0';
    fin.read( (char*)&DATA.FormatLength   , sizeof(DATA.FormatLength)   ); 
    fin.read( (char*)&DATA.FormatTag      , sizeof(DATA.FormatTag)      ); 
    fin.read( (char*)&DATA.nChannels      , sizeof(DATA.nChannels)      );  
    fin.read( (char*)&DATA.nSamplesPerSec , sizeof(DATA.nSamplesPerSec) ); 
    fin.read( (char*)&DATA.nAvgBytesPerSec, sizeof(DATA.nAvgBytesPerSec));  
    fin.read( (char*)&DATA.nBlockAlign    , sizeof(DATA.nBlockAlign)    );  
    fin.read( (char*)&DATA.BitsPerSample  , sizeof(DATA.BitsPerSample)  );  
    fin.read( (char*) DATA.s4             , 4*sizeof(unsigned char)     ); DATA.s4[4] = '\0';
    fin.read( (char*)&DATA.data_size      , sizeof(DATA.data_size)      );  

    switch (DATA.BitsPerSample)
    {
    case 8:

        DATA.cSamp.reserve(DATA.data_size);

        char temp;
        unsigned long nb_samples = DATA.data_size / sizeof(temp);
        for (unsigned long i=0; i<nb_samples; i++)
        {
            fin.read((char*)&temp, sizeof(temp));
            DATA.cSamp.push_back(temp); //I should have used iSamp here
        }

        break;
    case 16:

        DATA.sSamp.reserve(DATA.data_size);

        short temp;
        unsigned long nb_samples = DATA.data_size / sizeof(temp);
        for (unsigned long i=0; i<nb_samples; i++)
        {
            fin.read((char*)&temp, sizeof(temp));
            DATA.sSamp.push_back(temp); //I should have used iSamp here
        }

        break;
    case 32:

        DATA.iSamp.reserve(DATA.data_size);

        int temp;
        unsigned long nb_samples = DATA.data_size / sizeof(temp);
        for (unsigned long i=0; i<nb_samples; i++)
        {
            fin.read((char*)&temp, sizeof(temp));
            DATA.iSamp.push_back(temp);
        }

        break;
    }
}

int main()
{
    wavFile Buffer0;
    LoadWavFile("./voiceCreator/wav0.wav", Buffer0);
	//Do stuff with buffer
}


EDIT: I've added my solution to the code above with comments.
Last edited on
Either
1
2
3
boost::variant< std::vector<std::uint8_t>,
                std::vector<std::uint16_t>,
                std::vector<std::uint32_t>  >

or
1
2
3
boost::variant< std::reference_wrapper< std::vector<std::uint8_t> >,
                std::reference_wrapper< std::vector<std::uint16_t> >,
                std::reference_wrapper< std::vector<std::uint32_t> > >


is a possibility. http://www.boost.org/doc/libs/1_48_0/doc/html/variant.html
You could create a function template to get rid of all the code repetition. Something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<typename T>
vector<T> readData(ifstream& fin, unsigned long data_size)
{
	T temp;
	unsigned long nb_samples = data_size / sizeof(temp);

	vector<T> samp;
	samp.reserve(nb_samples);

	for (unsigned long i=0; i<nb_samples; i++)
	{
		fin.read((char*)&temp, sizeof(temp));
		samp.push_back(temp);
	}
	return samp;
}

And then your switch will look something like this:
1
2
3
4
5
6
7
8
9
10
11
12
switch (DATA.BitsPerSample)
{
case 8:
	DATA.cSamp = readData<char>(fin, DATA.data_size);
	break;
case 16:
	DATA.sSamp = readData<short>(fin, DATA.data_size);
	break;
case 32:
	DATA.iSamp = readData<int>(fin, DATA.data_size);
	break;
}
Last edited on
I think the vector<> is a member of the wavFile class. One could make the class itself a template - somewhat ungainly, since the number of bits would not be known at run-time till one actually reads the file.

Something more elaborate - a wavFile base class with virtual functions and template derived classes, parameterized on the element size - would be another option.
Thanks for the ideas guys.

JLBorges, the template derived classes with virtual functions is a bit above my skill level. Do you have a short example that I could study?

Peter87, I tried a version of the template<typename T> that you recommended by changing:
1
2
3
vector<char>  cSamp;
vector<short> sSamp;
vector<int>  iSamp;

To:
1
2
template<typename T>
vector<T> cSamp;


And then I kept the case statement, but used only cSamp for all three cases. Unfortunately this actually crashes my compiler (VS 2010) when I try to compile:
fatal error C1001: An internal error has occurred in the compiler.


The pre-compilation checking tells me:
Error: class "wavFile" has no member "cSamp"


My main goal is to get rid of sSamp and iSamp to make the analysis of the data easier to handle. I can live with an ugly switch statement in this read
I didn't see your first post there JLBorges.

if I replace
1
2
3
vector<char>  cSamp;
vector<short> sSamp;
vector<int>  iSamp;

with:
boost::variant<vector<char>, vector<short>, vector<int> > cSamp;

how do I access the members of cSamp? I've tried using:
DATA.cSamp.reserve(nb_samples);
but that gives:
error C2039: 'reserve' : is not a member of 'boost::variant<T0_,T1,T2>'
...


if I instead replace it with:
1
2
boost::variant<char,short,int> Types;
vector<Types> cSamp;

I get:
1>Snippets.cpp(24): error C2327: 'wavFile::Types' : is not a type name, static, or enumerator
1>Snippets.cpp(24): error C2327: 'wavFile::Types' : is not a type name, static, or enumerator
1>Snippets.cpp(24): error C2065: 'Types' : undeclared identifier
...


Edit: It appears my environment doesn't have std::reference_wrapper
Last edited on
> Do you have a short example that I could study?

Ok; perhaps sometime tomorrow. Or later tonight, if I get the time.

It would help if you could shed some light on what kind of analysis is done on the wave form.

In particular, the answer to: why can't a single std::vector<std::uint32_t> member be used to hold 8-bit, 16-bit or 32-bit data read from the file? Since there already is a member BitsPerSample which identifies the original bit-size of the data if it is required?
Brilliant! I can just store them all in a 32 bit container. I didn't think of that. Okay, you've given me a good lead. I'll give it a go.
This works well and it was really simple to implement. I just deleted the short and char vectors. Then replaced cSamp and sSamp with iSamp.

My only problem now is with fileIO stuff. I seem to get to the end-of-file way too early (after 54/37440 samples). I'm still working on this part, but the above issue is solved.
> the above issue is solved.

Great! We won't be needing anything more complicated now.



> My only problem now is with fileIO stuff.

ifstream fin(fname); You definitely need to open the stream in binary mode.

And wouldn't you have to take care of endianness for unformatted input?

Rather than try to work with 3 different types, which is hell, I would convert the file on the fly to one fixed type. Which would probably be 32-bit to avoid data loss.

IE: convert 8 and 16 bit samples to 32 bit upon load and just work only with 32 bit. If you need to modify and save the file later, you can convert back.
JLBorges, yes that solved it.
Disch, that's exactly what I did. I do save my file later in several segments so I convert it back as well. I've tested by splitting files in half and this appears to work well.
Topic archived. No new replies allowed.