Make PCM WAV reading (almost) machine endian neutral. The actual sample data read are still assumed to be in machine endian, which will produce garbage output on big endian archs.

Originally committed to SVN as r2222.
This commit is contained in:
Niels Martin Hansen 2008-07-03 02:22:18 +00:00
parent 8384e83e71
commit a7b64fe694

View file

@ -38,6 +38,7 @@
#include <wx/file.h> #include <wx/file.h>
#include "audio_provider_pcm.h" #include "audio_provider_pcm.h"
#include "utils.h" #include "utils.h"
#include "endian.h"
#include <stdint.h> #include <stdint.h>
#ifndef _WINDOWS #ifndef _WINDOWS
@ -240,7 +241,7 @@ class RiffWavPCMAudioProvider : public PCMAudioProvider {
private: private:
struct ChunkHeader { struct ChunkHeader {
char type[4]; char type[4];
uint32_t size; // XXX: Assume we're compiling on little endian uint32_t size;
}; };
struct RIFFChunk { struct RIFFChunk {
ChunkHeader ch; ChunkHeader ch;
@ -248,7 +249,7 @@ private:
}; };
struct fmtChunk { struct fmtChunk {
// Skip the chunk header here, it's processed separately // Skip the chunk header here, it's processed separately
uint16_t compression; // compression format used -- 0x01 = PCM uint16_t compression; // compression format used -- 0x0001 = PCM
uint16_t channels; uint16_t channels;
uint32_t samplerate; uint32_t samplerate;
uint32_t avg_bytes_sec; // can't always be trusted uint32_t avg_bytes_sec; // can't always be trusted
@ -276,7 +277,7 @@ public:
// Count how much more data we can have in the entire file // Count how much more data we can have in the entire file
// The first 4 bytes are already eaten by the header.format field // The first 4 bytes are already eaten by the header.format field
uint32_t data_left = header.ch.size - 4; uint32_t data_left = Endian::LittleToMachine(header.ch.size) - 4;
// How far into the file we have processed. // How far into the file we have processed.
// Must be incremented by the riff chunk size fields. // Must be incremented by the riff chunk size fields.
uint32_t filepos = sizeof(header); uint32_t filepos = sizeof(header);
@ -300,19 +301,21 @@ public:
fmtChunk &fmt = *(fmtChunk*)EnsureRangeAccessible(filepos, sizeof(fmtChunk)); fmtChunk &fmt = *(fmtChunk*)EnsureRangeAccessible(filepos, sizeof(fmtChunk));
if (fmt.compression != 1) throw _T("RIFF PCM WAV audio provider: Can't use file, not PCM encoding"); if (Endian::LittleToMachine(fmt.compression) != 1) throw _T("RIFF PCM WAV audio provider: Can't use file, not PCM encoding");
// Set stuff inherited from the AudioProvider class // Set stuff inherited from the AudioProvider class
sample_rate = fmt.samplerate; sample_rate = Endian::LittleToMachine(fmt.samplerate);
channels = fmt.channels; channels = Endian::LittleToMachine(fmt.channels);
bytes_per_sample = (fmt.significant_bits_sample + 7) / 8; // round up to nearest whole byte bytes_per_sample = (Endian::LittleToMachine(fmt.significant_bits_sample) + 7) / 8; // round up to nearest whole byte
} }
else if (strncmp(ch.type, "data", 4) == 0) { else if (strncmp(ch.type, "data", 4) == 0) {
// This won't pick up 'data' chunks inside 'wavl' chunks // This won't pick up 'data' chunks inside 'wavl' chunks
// since the 'wavl' chunks wrap those. // since the 'wavl' chunks wrap those.
int64_t samples = ch.size / bytes_per_sample; if (!got_fmt_header) throw _T("RIFF PCM WAV audio provider: Found 'data' chunk before 'fmt ' chunk, file is invalid.");
int64_t samples = Endian::LittleToMachine(ch.size) / bytes_per_sample;
int64_t frames = samples / channels; int64_t frames = samples / channels;
IndexPoint ip; IndexPoint ip;
@ -328,8 +331,8 @@ public:
// Update counters // Update counters
// Make sure they're word aligned // Make sure they're word aligned
data_left -= (ch.size + 1) & ~1; data_left -= (Endian::LittleToMachine(ch.size) + 1) & ~1;
filepos += (ch.size + 1) & ~1; filepos += (Endian::LittleToMachine(ch.size) + 1) & ~1;
} }
} }
}; };
@ -388,6 +391,7 @@ public:
// Now downmix // Now downmix
// Just average the samples over the channels (really bad if they're out of phase!) // Just average the samples over the channels (really bad if they're out of phase!)
// XXX: Assuming here that sample data are in machine endian, an upstream provider should ensure that
if (bytes_per_sample == 1) { if (bytes_per_sample == 1) {
uint8_t *src = (uint8_t *)tmp; uint8_t *src = (uint8_t *)tmp;
uint8_t *dst = (uint8_t *)buf; uint8_t *dst = (uint8_t *)buf;
@ -424,7 +428,6 @@ AudioProvider *CreatePCMAudioProvider(const wxString &filename)
AudioProvider *provider = 0; AudioProvider *provider = 0;
// Try Microsoft/IBM RIFF WAV first // Try Microsoft/IBM RIFF WAV first
// XXX: This is going to blow up if built on big endian archs
try { try {
provider = new RiffWavPCMAudioProvider(filename); provider = new RiffWavPCMAudioProvider(filename);
} }