|
|
@@ -8,6 +8,36 @@ namespace gameplay
|
|
|
// Audio buffer cache
|
|
|
static std::vector<AudioBuffer*> __buffers;
|
|
|
|
|
|
+// Callbacks for loading an ogg file using Stream
|
|
|
+static size_t readStream(void *ptr, size_t size, size_t nmemb, void *datasource)
|
|
|
+{
|
|
|
+ GP_ASSERT(datasource);
|
|
|
+ Stream* stream = reinterpret_cast<Stream*>(datasource);
|
|
|
+ return stream->read(ptr, size, nmemb);
|
|
|
+}
|
|
|
+
|
|
|
+static int seekStream(void *datasource, ogg_int64_t offset, int whence)
|
|
|
+{
|
|
|
+ GP_ASSERT(datasource);
|
|
|
+ Stream* stream = reinterpret_cast<Stream*>(datasource);
|
|
|
+ return !stream->seek(offset, whence);
|
|
|
+}
|
|
|
+
|
|
|
+static int closeStream(void *datasource)
|
|
|
+{
|
|
|
+ GP_ASSERT(datasource);
|
|
|
+ Stream* stream = reinterpret_cast<Stream*>(datasource);
|
|
|
+ stream->close();
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static long tellStream(void *datasource)
|
|
|
+{
|
|
|
+ GP_ASSERT(datasource);
|
|
|
+ Stream* stream = reinterpret_cast<Stream*>(datasource);
|
|
|
+ return stream->position();
|
|
|
+}
|
|
|
+
|
|
|
AudioBuffer::AudioBuffer(const char* path, ALuint buffer)
|
|
|
: _filePath(path), _alBuffer(buffer)
|
|
|
{
|
|
|
@@ -63,8 +93,8 @@ AudioBuffer* AudioBuffer::create(const char* path)
|
|
|
}
|
|
|
|
|
|
// Load sound file.
|
|
|
- FILE* file = FileSystem::openFile(path, "rb");
|
|
|
- if (!file)
|
|
|
+ std::auto_ptr<Stream> stream(FileSystem::open(path));
|
|
|
+ if (stream.get() == NULL || !stream->canRead())
|
|
|
{
|
|
|
GP_ERROR("Failed to load audio file %s.", path);
|
|
|
goto cleanup;
|
|
|
@@ -72,7 +102,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
|
|
|
|
|
|
// Read the file header
|
|
|
char header[12];
|
|
|
- if (fread(header, 1, 12, file) != 12)
|
|
|
+ if (stream->read(header, 1, 12) != 12)
|
|
|
{
|
|
|
GP_ERROR("Invalid header for audio file %s.", path);
|
|
|
goto cleanup;
|
|
|
@@ -81,7 +111,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
|
|
|
// Check the file format
|
|
|
if (memcmp(header, "RIFF", 4) == 0)
|
|
|
{
|
|
|
- if (!AudioBuffer::loadWav(file, alBuffer))
|
|
|
+ if (!AudioBuffer::loadWav(stream.get(), alBuffer))
|
|
|
{
|
|
|
GP_ERROR("Invalid wave file: %s", path);
|
|
|
goto cleanup;
|
|
|
@@ -89,7 +119,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
|
|
|
}
|
|
|
else if (memcmp(header, "OggS", 4) == 0)
|
|
|
{
|
|
|
- if (!AudioBuffer::loadOgg(file, alBuffer))
|
|
|
+ if (!AudioBuffer::loadOgg(stream.get(), alBuffer))
|
|
|
{
|
|
|
GP_ERROR("Invalid ogg file: %s", path);
|
|
|
goto cleanup;
|
|
|
@@ -101,10 +131,6 @@ AudioBuffer* AudioBuffer::create(const char* path)
|
|
|
goto cleanup;
|
|
|
}
|
|
|
|
|
|
- //NOTE: loadOgg actually sets this null, so it is expected
|
|
|
- if (file)
|
|
|
- fclose(file);
|
|
|
-
|
|
|
buffer = new AudioBuffer(path, alBuffer);
|
|
|
|
|
|
// Add the buffer to the cache.
|
|
|
@@ -113,34 +139,32 @@ AudioBuffer* AudioBuffer::create(const char* path)
|
|
|
return buffer;
|
|
|
|
|
|
cleanup:
|
|
|
-
|
|
|
- if (file)
|
|
|
- fclose(file);
|
|
|
if (alBuffer)
|
|
|
AL_CHECK( alDeleteBuffers(1, &alBuffer) );
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
|
|
|
+bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
|
|
|
{
|
|
|
- GP_ASSERT(file);
|
|
|
- unsigned char stream[12];
|
|
|
+ GP_ASSERT(stream);
|
|
|
+
|
|
|
+ unsigned char data[12];
|
|
|
|
|
|
// Verify the wave fmt magic value meaning format.
|
|
|
- if (fread(stream, 1, 8, file) != 8 || memcmp(stream, "fmt ", 4) != 0 )
|
|
|
+ if (stream->read(data, 1, 8) != 8 || memcmp(data, "fmt ", 4) != 0 )
|
|
|
{
|
|
|
GP_ERROR("Failed to verify the magic value for the wave file format.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
unsigned int section_size;
|
|
|
- section_size = stream[7]<<24;
|
|
|
- section_size |= stream[6]<<16;
|
|
|
- section_size |= stream[5]<<8;
|
|
|
- section_size |= stream[4];
|
|
|
+ section_size = data[7]<<24;
|
|
|
+ section_size |= data[6]<<16;
|
|
|
+ section_size |= data[5]<<8;
|
|
|
+ section_size |= data[4];
|
|
|
|
|
|
// Check for a valid pcm format.
|
|
|
- if (fread(stream, 1, 2, file) != 2 || stream[1] != 0 || stream[0] != 1)
|
|
|
+ if (stream->read(data, 1, 2) != 2 || data[1] != 0 || data[0] != 1)
|
|
|
{
|
|
|
GP_ERROR("Unsupported audio file format (must be a valid PCM format).");
|
|
|
return false;
|
|
|
@@ -148,31 +172,31 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
|
|
|
|
|
|
// Get the channel count (16-bit little-endian).
|
|
|
int channels;
|
|
|
- if (fread(stream, 1, 2, file) != 2)
|
|
|
+ if (stream->read(data, 1, 2) != 2)
|
|
|
{
|
|
|
GP_ERROR("Failed to read the wave file's channel count.");
|
|
|
return false;
|
|
|
}
|
|
|
- channels = stream[1]<<8;
|
|
|
- channels |= stream[0];
|
|
|
+ channels = data[1]<<8;
|
|
|
+ channels |= data[0];
|
|
|
|
|
|
// Get the sample frequency (32-bit little-endian).
|
|
|
ALuint frequency;
|
|
|
- if (fread(stream, 1, 4, file) != 4)
|
|
|
+ if (stream->read(data, 1, 4) != 4)
|
|
|
{
|
|
|
GP_ERROR("Failed to read the wave file's sample frequency.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- frequency = stream[3]<<24;
|
|
|
- frequency |= stream[2]<<16;
|
|
|
- frequency |= stream[1]<<8;
|
|
|
- frequency |= stream[0];
|
|
|
+ frequency = data[3]<<24;
|
|
|
+ frequency |= data[2]<<16;
|
|
|
+ frequency |= data[1]<<8;
|
|
|
+ frequency |= data[0];
|
|
|
|
|
|
// The next 6 bytes hold the block size and bytes-per-second.
|
|
|
// We don't need that info, so just read and ignore it.
|
|
|
// We could use this later if we need to know the duration.
|
|
|
- if (fread(stream, 1, 6, file) != 6)
|
|
|
+ if (stream->read(data, 1, 6) != 6)
|
|
|
{
|
|
|
GP_ERROR("Failed to read past the wave file's block size and bytes-per-second.");
|
|
|
return false;
|
|
|
@@ -180,13 +204,13 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
|
|
|
|
|
|
// Get the bit depth (16-bit little-endian).
|
|
|
int bits;
|
|
|
- if (fread(stream, 1, 2, file) != 2)
|
|
|
+ if (stream->read(data, 1, 2) != 2)
|
|
|
{
|
|
|
GP_ERROR("Failed to read the wave file's bit depth.");
|
|
|
return false;
|
|
|
}
|
|
|
- bits = stream[1]<<8;
|
|
|
- bits |= stream[0];
|
|
|
+ bits = data[1]<<8;
|
|
|
+ bits |= data[0];
|
|
|
|
|
|
// Now convert the given channel count and bit depth into an OpenAL format.
|
|
|
ALuint format = 0;
|
|
|
@@ -216,7 +240,7 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
|
|
|
unsigned int length = section_size - 16;
|
|
|
|
|
|
// Extension size is 2 bytes.
|
|
|
- if (fread(stream, 1, length, file) != length)
|
|
|
+ if (stream->read(data, 1, length) != length)
|
|
|
{
|
|
|
GP_ERROR("Failed to read extension size from wave file.");
|
|
|
return false;
|
|
|
@@ -227,32 +251,32 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
|
|
|
while (true)
|
|
|
{
|
|
|
// Check if we are at the end of the file without reading the data.
|
|
|
- if (feof(file))
|
|
|
+ if (stream->eof())
|
|
|
{
|
|
|
GP_ERROR("Failed to load wave file; file appears to have no data.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// Read in the type of the next section of the file.
|
|
|
- if (fread(stream, 1, 4, file) != 4)
|
|
|
+ if (stream->read(data, 1, 4) != 4)
|
|
|
{
|
|
|
GP_ERROR("Failed to read next section type from wave file.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// Data chunk.
|
|
|
- if (memcmp(stream, "data", 4) == 0)
|
|
|
+ if (memcmp(data, "data", 4) == 0)
|
|
|
{
|
|
|
// Read how much data is remaining and buffer it up.
|
|
|
unsigned int dataSize;
|
|
|
- if (fread(&dataSize, sizeof(int), 1, file) != 1)
|
|
|
+ if (stream->read(&dataSize, sizeof(int), 1) != 1)
|
|
|
{
|
|
|
GP_ERROR("Failed to read size of data section from wave file.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
char* data = new char[dataSize];
|
|
|
- if (fread(data, sizeof(char), dataSize, file) != dataSize)
|
|
|
+ if (stream->read(data, sizeof(char), dataSize) != dataSize)
|
|
|
{
|
|
|
GP_ERROR("Failed to load wave file; file is missing data.");
|
|
|
SAFE_DELETE_ARRAY(data);
|
|
|
@@ -281,22 +305,22 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
|
|
|
{
|
|
|
// Store the name of the chunk so we can report errors informatively.
|
|
|
char chunk[5] = { 0 };
|
|
|
- memcpy(chunk, stream, 4);
|
|
|
+ memcpy(chunk, data, 4);
|
|
|
|
|
|
// Read the chunk size.
|
|
|
- if (fread(stream, 1, 4, file) != 4)
|
|
|
+ if (stream->read(data, 1, 4) != 4)
|
|
|
{
|
|
|
GP_ERROR("Failed to read size of '%s' chunk from wave file.", chunk);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- section_size = stream[3]<<24;
|
|
|
- section_size |= stream[2]<<16;
|
|
|
- section_size |= stream[1]<<8;
|
|
|
- section_size |= stream[0];
|
|
|
+ section_size = data[3]<<24;
|
|
|
+ section_size |= data[2]<<16;
|
|
|
+ section_size |= data[1]<<8;
|
|
|
+ section_size |= data[0];
|
|
|
|
|
|
// Seek past the chunk.
|
|
|
- if (fseek(file, section_size, SEEK_CUR) != 0)
|
|
|
+ if (stream->seek(section_size, SEEK_CUR) == false)
|
|
|
{
|
|
|
GP_ERROR("Failed to seek past '%s' chunk in wave file.", chunk);
|
|
|
return false;
|
|
|
@@ -304,10 +328,10 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
|
|
|
+
|
|
|
+bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
|
|
|
{
|
|
|
- GP_ASSERT(file);
|
|
|
+ GP_ASSERT(stream);
|
|
|
|
|
|
OggVorbis_File ogg_file;
|
|
|
vorbis_info* info;
|
|
|
@@ -316,11 +340,16 @@ bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
|
|
|
int section;
|
|
|
long size = 0;
|
|
|
|
|
|
- rewind(file);
|
|
|
+ stream->rewind();
|
|
|
|
|
|
- if ((result = ov_open(file, &ogg_file, NULL, 0)) < 0)
|
|
|
+ ov_callbacks callbacks;
|
|
|
+ callbacks.read_func = readStream;
|
|
|
+ callbacks.seek_func = seekStream;
|
|
|
+ callbacks.close_func = closeStream;
|
|
|
+ callbacks.tell_func = tellStream;
|
|
|
+
|
|
|
+ if ((result = ov_open_callbacks(stream, &ogg_file, NULL, 0, callbacks)) < 0)
|
|
|
{
|
|
|
- fclose(file);
|
|
|
GP_ERROR("Failed to open ogg file.");
|
|
|
return false;
|
|
|
}
|
|
|
@@ -367,9 +396,6 @@ bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
|
|
|
SAFE_DELETE_ARRAY(data);
|
|
|
ov_clear(&ogg_file);
|
|
|
|
|
|
- // ov_clear actually closes the file pointer as well.
|
|
|
- file = 0;
|
|
|
-
|
|
|
return true;
|
|
|
}
|
|
|
|