Hey all,
We've updated our game on Github to use SFML as a submodule. The version of SFML that we were using before was SFML 2.2 and everything worked fine with the music loop in our level. With the new SFML version however, the music loop didn't run smoothly and when I looked into the logs, the following message appeared after every loop:
An internal OpenAL call failed in SoundStream.cpp(435).
Expression:
alBufferData(buffer, m_format, data.samples, size, m_sampleRate)
Error description:
AL_INVALID_VALUE
A numeric argument is out of range.
The code we are using to instantiate the music looks like this:
// the music path references a .wav file
if (m_backgroundMusic.openFromFile(m_currentLevel.getMusicPath()))
{
m_backgroundMusic.setLoop(true);
m_backgroundMusic.setVolume(g_resourceManager->getConfiguration().volume);
m_backgroundMusic.play();
}
Has anyone an idea why the AL check might fail? Any help is greatly appreciated.
I dug into it a bit.
I've got a Python script online ( http://blog.theroyweb.com/extracting-wav-file-header-information-using-a-python-script ) and checked out the wav file:
python wav.py cave_try.wav
Subchunks Found:
fmt , data, smpl, acid, LIST,
Data Chunk located at offset [36] of data length [9376744] bytes
ByteRate: 176400
BitsPerSample: 16
NumChannels: 2
Subchunk1Size: 16
ChunkSize: 9376914
Format: WAVE
AudioFormat: 1
BlockAlign: 4
SampleRate: 44100
Filename: cave_try.wav
The data length is 9376744 bytes.
But I checked and SFML wav file reader reads 9376878 bytes from the wav in total.
I also checked and ogg file has exactly 13772 samples in last array it passes to OpenAL, while wav has 13839.
This checks out: SFML reads 9376878 - 9376744 = 134 extra bytes and since samples are 16 bit it's exactly 13839 - 13772 = 67 samples, 2 bytes per sample, 134 bytes.
If I hardcode it to change size of last array to 13772 when count is 13839 (cutting off the extra 67 samples) then it works perfectly for wav too.
So SFML assumes that last chunk is data till end of file and reads it blindly, which results in reading some chunks that are other stuff as samples, which produces the extra samples that cause stutter and OpenAL error.
I have no idea why it casues OpenAL error though.
This error has nothing to do with looping, BTW. If you play the wav just once it'll stutter at the end and cause the OpenAL error too.
And this is what last 134 bytes are in hex and ascii:
[frex@localhost audiobug]$ tail --bytes=134 cave_try.wav | xxd -g 1
00000000: 73 6d 70 6c 3c 00 00 00 00 00 00 00 00 00 00 00 smpl<...........
00000010: 94 58 00 00 3c 00 00 00 00 00 00 00 00 00 00 00 .X..<...........
00000020: 00 00 00 00 01 00 00 00 00 00 00 00 00 00 02 00 ................
00000030: 00 04 00 00 00 00 00 00 f9 c4 23 00 00 00 00 00 ..........#.....
00000040: 00 00 00 00 61 63 69 64 18 00 00 00 00 00 00 00 ....acid........
00000050: 3c 00 80 00 00 00 00 00 50 00 00 00 04 00 04 00 <.......P.......
00000060: 9a 99 b4 42 4c 49 53 54 1a 00 00 00 49 4e 46 4f ...BLIST....INFO
00000070: 49 53 46 54 0d 00 00 00 46 4c 20 53 74 75 64 69 ISFT....FL Studi
00000080: 6f 20 31 31 00 00 o 11..
Looks too good as ascii (FL Studio 11 :P) to be samples so it's probably metadata and the like.
Edit: Oh, also - Sound does not have this problem, because SoundBuffer uses InputSoundFile::getSampleCount which returns correct value. Music has the problem because it keeps calling the reader, which in case of wav reader goes YOLO till end of file, reading in some additional data that it shouldn't. That's why the bug sat there undetected for so long, wav for sounds works fine, and for music most people use ogg because of file size.
The only thing I've noticed when quickly debugging it, is that data.samples is slightly different between the original one and the one from Audacity: 27648 vs 27678
data.samples as in the value of samples itself? Or did you mean count (that number is way too little to be sample count for such a long file BTW)? Anyway - Hapax's file does not cause OpenAL error but it also has extra non data bytes at the end (112 of them) that SFML treats as samples. And Hapax's and original ogg are same, exactly same, byte for byte. And all four files (wav and ogg, both Hapax's and OP's) correctly return 4688372 as sample count from InputSoundFile::getSampleCount.
This would fix this bug.
It adds a check before reading each sample in the loop in read to see if the wav data section ended (using same way to turn sample count to offset like seek function already does).
diff --git a/src/SFML/Audio/SoundFileReaderWav.cpp b/src/SFML/Audio/SoundFileReaderWav.cpp
index 5323f51..4e86f5b 100644
--- a/src/SFML/Audio/SoundFileReaderWav.cpp
+++ b/src/SFML/Audio/SoundFileReaderWav.cpp
@@ -110,7 +110,8 @@ bool SoundFileReaderWav::check(InputStream& stream)
SoundFileReaderWav::SoundFileReaderWav() :
m_stream (NULL),
m_bytesPerSample(0),
-m_dataStart (0)
+m_dataStart (0),
+m_sampleCount (0)
{
}
@@ -145,7 +146,7 @@ Uint64 SoundFileReaderWav::read(Int16* samples, Uint64 maxCount)
assert(m_stream);
Uint64 count = 0;
- while (count < maxCount)
+ while (count < maxCount && m_stream->tell() < m_dataStart + m_sampleCount * m_bytesPerSample)
{
switch (m_bytesPerSample)
{
@@ -283,7 +284,8 @@ bool SoundFileReaderWav::parseHeader(Info& info)
// "data" chunk
// Compute the total number of samples
- info.sampleCount = subChunkSize / m_bytesPerSample;
+ m_sampleCount = subChunkSize / m_bytesPerSample;
+ info.sampleCount = m_sampleCount;
// Store the starting position of samples in the file
m_dataStart = m_stream->tell();
diff --git a/src/SFML/Audio/SoundFileReaderWav.hpp b/src/SFML/Audio/SoundFileReaderWav.hpp
index a52bc1f..a824cdb 100644
--- a/src/SFML/Audio/SoundFileReaderWav.hpp
+++ b/src/SFML/Audio/SoundFileReaderWav.hpp
@@ -111,6 +111,7 @@ private:
InputStream* m_stream; ///< Source stream to read from
unsigned int m_bytesPerSample; ///< Size of a sample, in bytes
Uint64 m_dataStart; ///< Starting position of the audio data in the open file
+ Uint64 m_sampleCount;
};
} // namespace priv