No mod, xm or s3m support?
I am disappoint, otherwise orgasmic
I've written an implementation for sf::SoundStream and sf::SoundBuffer using libmodplug. I can't find the link to the github-page, but here's the code
//xmsound.h
#ifndef SLC_CONTENT_XMSOUND_H
#define SLC_CONTENT_XMSOUND_H
#include <SFML/Audio/SoundStream.hpp>
#include <libmodplug/modplug.h>
#include "core/filesystem/path.h"
namespace sf{
class SoundBuffer;
}
namespace content{
/**
* Opens an XM into a sf::SoundBuffer
* @param [in] path the path of the XM-file
* @param [out] the sf::SoundBuffer to load the XM into
* @return true if successful, false otherwise
*/
extern bool openXMFile(const core::filesystem::path &path, sf::SoundBuffer &buf);
/**
* @brief a sf::SoundStream for XM
*
* This class extends sf::SoundStream for XM files.
*/
class XMStream : public sf::SoundStream
{
public:
XMStream();
~XMStream();
//bool Open(const std::string &path);
bool Open(const core::filesystem::path &path);
private:
bool OnGetData(Chunk& data);
void OnSeek(float timeOffset){ ModPlug_Seek(mod, timeOffset * 1000);}
private:
ModPlugFile *mod;
int singleSampleSize, samplesSize;
char *samples;
};
}
#endif
//xmsound.cpp
#include <fstream>
#include <string>
#include <SFML/Audio/SoundBuffer.hpp>
#include <libmodplug/modplug.h>
#include "content/xmsound.h"
#include "core/staticconstructor.h"
#include "core/filesystem/path.h"
namespace content{
STATIC_INIT()
{
ModPlug_Settings modSettings;
ModPlug_GetSettings(&modSettings);
modSettings.mBits = 16;
ModPlug_SetSettings(&modSettings);
}
static bool parseXMFile(const core::filesystem::path &path, ModPlugFile *&mod)
{
// first we read the whole file (modFile) into a buffer (fileData)
std::ifstream modFile(path.string().c_str(), std::ifstream::in | std::ifstream::binary);
if(!modFile.is_open())
return false;
modFile.seekg(0, std::ifstream::end);
const std::streampos fileSize = modFile.tellg();
modFile.seekg(0, std::ifstream::beg);
char *fileData = new char[fileSize];
modFile.readsome(fileData, fileSize);
if(!modFile)
{
delete[] fileData;
return false;
}
modFile.close();
// now we let modplug handle this data
mod = ModPlug_Load(fileData, fileSize);
delete[] fileData;
if(!mod)
return false;
return true;
}
bool openXMFile(const core::filesystem::path &path, sf::SoundBuffer &buf)
{
ModPlugFile *mod;
if(!parseXMFile(path, mod))
return false;
ModPlug_Settings modSettings;
ModPlug_GetSettings(&modSettings);
// one sample (a single sound so to say) is made up out of the bits per individual sound * number of channels (mono, stereo, ..)
const int sampleSize = modSettings.mChannels * (modSettings.mBits / 8);
const int guessedSize = modSettings.mFrequency * sampleSize * (ModPlug_GetLength(mod) * 0.001f);
std::vector<char> samples;
samples.reserve(guessedSize);
char *sample = new char[sampleSize];
// ModPlug_GetLength deems itself unreliable; it is not said how. It will lead to appended silence if guessed length > actual length
// we read only sample by sample, to be safe. Non-streamed content loading should not be done in a performance-critical area anyways.
#if 0
ModPlug_Read(mod, sample, guessedSize);
samples.insert(samples.end(), sample, sample + guessedSize);
#endif
// OBSOLETE | we already allocated memory, why reallocate to something smaller?
#if 0
delete[] sample;
sample = new char[sampleSize];
#endif
// while something was read, store it in vector
while(ModPlug_Read(mod, sample, sampleSize))
samples.insert(samples.end(), sample, sample + sampleSize);
ModPlug_Unload(mod);
delete[] sample;
// [url]http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#69[/url]
#if 0
char* sampleArray = new char[samples.size()];
std::copy(samples.begin(), samples.end(), sampleArray);
#endif
// we just convert to sf::Int16* (16-bit samples); as sizeof(sf::Int16) > sizeof(char) we also need take care of the new size
return buf.LoadFromSamples(reinterpret_cast<const sf::Int16*>(&samples.front()), samples.size() / sizeof(sf::Int16), modSettings.mChannels, modSettings.mFrequency);
}
XMStream::XMStream()
:samples(NULL){}
XMStream::~XMStream()
{
delete[] samples;
// GetSampleRate is an indicator if a sound was present
// TODO: couldn't we use samples as an indicator?
if(GetSampleRate() != 0)
ModPlug_Unload(mod);
}
bool XMStream::Open(const core::filesystem::path &path)
{
// TODO: what if XMStream already open?
if(!parseXMFile(path, mod))
return false;
ModPlug_Settings modSettings;
ModPlug_GetSettings(&modSettings);
singleSampleSize = modSettings.mChannels * (modSettings.mBits / 8);
samplesSize = singleSampleSize * (modSettings.mFrequency / 10); // 1 / 10 second chunk
// ^- note: this might cause up to 1 / 10 - (1 / (modSettings.mFrequency / 10)) seconds unwanted silence at the end of the song!
// ^- I think it's worth it instead of reading sample by sample here ;z33ky
Initialize(modSettings.mChannels, modSettings.mFrequency);
return true;
}
bool XMStream::OnGetData(Chunk& data)
{
// char *samples = NULL; // member for workaround for delete[] (see below)
// TODO: delete every fucking time?! ;z33ky
delete[] samples;
samples = new char[samplesSize];
// note: in theory, ModPlug_Read should return the number of bytes read, at least it informs us when nothing was read
data.NbSamples = (ModPlug_Read(mod, samples, samplesSize) / singleSampleSize) * sizeof(sf::Int16);
data.Samples = reinterpret_cast<sf::Int16*>(samples);
// delete[] samples; // seems to sometimes make problems (only at song start?)?
// ^- worked around that ;z33ky
return data.NbSamples != 0;
}
}
The implementation for sf::SoundBuffer looks a bit questionable... but it did work.
STATIC_INIT simply creates a static object and the code seen represents its constructor.
Also, an version of SFML 2 used creating this.
Use it however you want, no credits whatsoever required.