1
votes

I'm trying to load a WAVE file into a data structure, then save it back to disk as a copy of the original. Loading and saving appears to work fine but the copied WAVE file won't play any sound (although It does open up without errors). I'm wondering if this has anything to do with endian-ness?

This is my data structure to hold the WAVE file:

struct WaveFile
{
public:

    static const uint16 NUM_CHARS = 4;

public:

    WaveFile() : Data(nullptr) {}

    ~WaveFile() { delete[] Data; }

    char ChunkID[NUM_CHARS];

    uint32 ChunkSize;

    char Format[NUM_CHARS];

    char SubChunkID[NUM_CHARS];

    uint32 SubChunkSize;

    uint16 AudioFormat;

    uint16 NumChannels;

    uint32 SampleRate;

    uint32 ByteRate;

    uint16 BlockAlign;

    uint16 BitsPerSample;

    char SubChunk2ID[NUM_CHARS];

    uint32 SubChunk2Size;

    byte* Data; 
};

This is how I load it in:

std::ifstream file(filename, std::ios::binary);

if (file.good())
{
    file.read(waveFile.ChunkID, WaveFile::NUM_CHARS);
    file.read(reinterpret_cast<char*>(&waveFile.ChunkSize), size_ui32);
    file.read(waveFile.Format, WaveFile::NUM_CHARS);
    file.read(waveFile.SubChunkID, WaveFile::NUM_CHARS);
    file.read(reinterpret_cast<char*>(&waveFile.SubChunkSize), size_ui32);
    file.read(reinterpret_cast<char*>(&waveFile.AudioFormat), size_ui16);
    file.read(reinterpret_cast<char*>(&waveFile.NumChannels), size_ui16);
    file.read(reinterpret_cast<char*>(&waveFile.SampleRate), size_ui32);
    file.read(reinterpret_cast<char*>(&waveFile.ByteRate), size_ui32);
    file.read(reinterpret_cast<char*>(&waveFile.BlockAlign), size_ui16);
    file.read(reinterpret_cast<char*>(&waveFile.BitsPerSample), size_ui16);
    file.read(waveFile.SubChunk2ID, WaveFile::NUM_CHARS);
    file.read(reinterpret_cast<char*>(&waveFile.SubChunk2Size), size_ui32);
    waveFile.Data = new byte[waveFile.SubChunk2Size];
    file.read(reinterpret_cast<char*>(waveFile.Data), sizeof(waveFile.SubChunk2Size));
    file.close();
}

And this is how I write the data back out to a file:

std::ofstream file(outfile, std::ios::binary);

if (file.good())
{
    file.flush();
    file.write(waveFile.ChunkID, WaveFile::NUM_CHARS);
    file.write(reinterpret_cast<const char*>(&waveFile.ChunkSize), size_ui32);
    file.write(waveFile.Format, WaveFile::NUM_CHARS);
    file.write(waveFile.SubChunkID, WaveFile::NUM_CHARS);
    file.write(reinterpret_cast<const char*>(&waveFile.SubChunkSize), size_ui32);
    file.write(reinterpret_cast<const char*>(&waveFile.AudioFormat), size_ui16);
    file.write(reinterpret_cast<const char*>(&waveFile.NumChannels), size_ui16);
    file.write(reinterpret_cast<const char*>(&waveFile.SampleRate), size_ui32);
    file.write(reinterpret_cast<const char*>(&waveFile.ByteRate), size_ui32);
    file.write(reinterpret_cast<const char*>(&waveFile.BlockAlign), size_ui16);
    file.write(reinterpret_cast<const char*>(&waveFile.BitsPerSample), size_ui16);
    file.write(waveFile.SubChunk2ID, WaveFile::NUM_CHARS);
    file.write(reinterpret_cast<const char*>(&waveFile.SubChunk2Size), size_ui32);
    file.write(reinterpret_cast<const char*>(waveFile.Data), waveFile.SubChunk2Size);
    file.close();   
}

The size of the copy is the same as the original also. If anyone is wondering, uint16, uint32 and byte are just typedefs for unsigned short, unsigned int and unsigned char. size_ui32 variables are just this:

static const uint32 size_ui32 = sizeof(uint32);
static const uint32 size_ui16 = sizeof(uint16);

That't about it, can't seem to find out where I've gone wrong.

Cheers for any help.

2

2 Answers

1
votes

This works on most wavefiles, but some headers may contain specific user data. You can use sigle write of struct WAVEHEADER:

struct WAVEFILEHEADER
{
    // don't change the order of attributes
    char m_lpcChunkId[4];
    int m_iChunkSize;
    char m_lpcFormat[4];
    char m_lpcSubChunkFmt[4];
    int m_iSubChunkFmtSize;
    short int m_siAudioFormat;
    short int m_siNumChannels;

    int m_iSampleRate;
    int m_iByteRate;
    short int m_siBlockAlign;
    short int m_siBitsPerSample;

    char m_lpcChunkData[4];
    int m_iSubChunkDataSize;
    // unsigned char * m_data;
};

WAVEFILEHEADER whdr;
FILE* fid = fopen("newaudio.wav","wb"); // important to use binary file

// some inicialization for whdr - RIFF etc.
// You can use only 1 write to save whole header:
fwrite(&whdr, 1, sizeof(WAVEFILEHEADER), fid);

// or you can read wav header
fread(&whdr, 1, sizeof(WAVEFILEHEADER), fid); 
// check chunk size, few headers have aditional 2 bytes (by definition user data, but they was always 0x0000)
0
votes

After posting this question I've just discovered the problem.

When reading the Data in i've done this:

file.read(reinterpret_cast<char*>(waveFile.Data), sizeof(waveFile.SubChunk2Size));

which should be:

file.read(reinterpret_cast<char*>(waveFile.Data), waveFile.SubChunk2Size);  

Worked when I removed the sizeof(). Silly me.