[SOLVED]FMOD does not play a sound from qresource?
-
Hello,
I am making a sound player in FMOD with Qt. The code is simple:bool FMPlayer::systemInit(const char *fname) { FMOD::Channel* channel; FMOD::System* system; FMOD::Sound* sound; FMOD::System_Create(&system); system->init(32, FMOD_INIT_NORMAL, 0); system->createSound(fname, 0, 0, &sound); sound->setMode(FMOD_LOOP_OFF); system->playSound(sound, false, 0, &channel); }
If I pass my hard drive full path as a parameter the sound is played. But if I use
FMPlayer:: systemInit(":/resources/sounds/blop.wav");
It plays nothing. Why is that?
-
":/resources/sounds/blop.wav"
This path format is a Qt mechanism. FMOD knows nothing about it and doesn't understand Qt embedded resources.
What you can do is use a QFile to readAll() the sound into a QByteArray.
createSound() takes a mode parameter. Pass FMOD_OPENMEMORY_POINT there and use the data() pointer of the array as the first argument to createSound(), as described in the FMOD docs
If you don't want to keep the array around then use FMOD_OPENMEMORY instead of FMOD_OPENMEMORY_POINT and FMOD will copy the data and take care of it.Btw. You should really check return codes of FMOD functions. Each call can fail and if you don't check which is it then debugging such code is pretty much pure guesswork.
-
Thank you. Before you replied I got the idea of output to byte array. I want to ask something else- can I redirect the resources I have ( png, wav, xml ) to different thing than the exe of my program?
-
Redirect? What do you mean?
-
Well, when I use qt resource system, the files, pictures, music and so go into the executable binary. So I was wondering if I can move them to archive like structure and read from there.
-
-Well sure, but this has nothing to do with Qt resource system. It is only for embedding resources in executables or libraries.-
EDIT: I forgot that Qt also allows you to create "external resource packages":https://qt-project.org/doc/qt-5-snapshot/resources.html#external-binary-resources
You would have to provide some mechanism for extracting them from such archive, but that is out of Qt scope (apart maybe from using QFile to access such file). If I rememer correctly FMOD can also stream data from external files and you can give it an offset into the file and size. Should be easy enough to "glue" together files and provide some index-like structure for it.
FMOD also provides a higher level API - FMOD Studio, which packages sounds into single file banks and manages compression, extraction etc. Maybe you could switch to that? It's quite pleasant to work with in my experience.
-
EDIT: I forgot that Qt also allows you to create "external resource packages":https://qt-project.org/doc/qt-5-snapshot/resources.html#external-binary-resources
Thanks for this.
Another one. I need a bit of help here. This is test for sound. However the FMPlayer::systemInit() fails me.QFile io(":/resources/sounds/blop.wav"); if ( !io.open(QFile::ReadOnly)) { qDebug() << "Error loading file"; } { QByteArray qba; qba = io.readAll(); const char *begin = qba.constData(); if ( FMPlayer:: systemInit(begin) ) { qDebug() << "Played sound"; } else { qDebug() << "FMOD failed"; } } /* END FMOD TEST */
And here is my FMPlayer::systemInit(...):
bool FMPlayer::systemInit(const char *fname) { FMOD::Channel* channel; FMOD::System* system; FMOD::Sound* sound; FMOD::System_Create(&system); system->init(32, FMOD_INIT_NORMAL, 0); if ( FMOD_OK == system->createSound(fname, FMOD_OPENMEMORY, 0, &sound) ) { sound->setMode(FMOD_LOOP_OFF); system->playSound(sound, false, 0, &channel); return true; } else { return false; } }
I am sure I am making some mistake, but can't figure it out... I'd really appreciate the help.
-
As I said: read the docs ;)
I'll quote the relevant part:
Note that FMOD_OPENRAW, FMOD_OPENMEMORY, FMOD_OPENMEMORY_POINT and FMOD_OPENUSER will not work here without the exinfo structure present, as more information is needed.
When you use a FMOD_OPENMEMORY flag you also need to pass a pointer to FMOD_CREATESOUNDEXINFO structure with a single field filled in: the length of the data.
-
As I said: "read the docs":http://www.fmod.org/documentation/#content/generated/FMOD_System_CreateSound.html ;)
I'll quote the relevant part:Note that FMOD_OPENRAW, FMOD_OPENMEMORY, FMOD_OPENMEMORY_POINT and FMOD_OPENUSER will not work here without the exinfo structure present, as more information is needed.
When you use a FMOD_OPENMEMORY flag you also need to pass a pointer to FMOD_CREATESOUNDEXINFO structure with a single field filled in: the length of the data.[/quote]
You were in a word "saviour". I learned about FMOD yesterday so I had almost no time to track all the stuff. Now I get it - the FMOD_SOUNDEXINFO must be some header for info... It makes sense. The file can be from ogg to wav. Thanks. The problem is [SOLVED].
P.S. I had to rewrote my function to recieve QByteArray::size() for this... -
For anyone else stuck here. I give you the gift of working code.
m_system
is a pointer to the FMOD system
m_sound
is a pointer to the FMOD soundvoid Sound::createSoundFromResource(std::string filePath) { // get the file based on the path QFile resourceFile(QString::fromStdString(filePath)); Q_ASSERT_X(resourceFile.exists(), "createSoundFromResource", "the filepath does not exist"); // open the file and read all the data from it if (resourceFile.open(QIODevice::ReadOnly)) { auto data = resourceFile.readAll(); // set metadata about the file so FMOD can read it FMOD_CREATESOUNDEXINFO* exinfo = new FMOD_CREATESOUNDEXINFO(); exinfo->length = static_cast<unsigned int>(data.length()); exinfo->cbsize = sizeof(FMOD_CREATESOUNDEXINFO); // the unmentioned line which was killing me auto result = FMOD_System_CreateSound(m_system, data.data(), FMOD_OPENMEMORY, exinfo, &m_sound); SoundWrapper::CheckError(result); } else { Q_ASSERT_X(false, "createSoundFromResource", "opening the files failed"); } }