use OGG sounds not saved to disk?

Before I asked about loading textures the same way.
Now I have a string of ogg file (which you can get by open(‘file.ogg’).read() ).
I’m hoping there’s a similar way to use them without having them saved to disk first.
I have all sorts of sound files get like this for sound effects, as well as DirectGUI sounds.

Since sound files are handled by the underlying sound library, instead of directly by Panda, it is not as easy to open one with string data instead of a file.

However, if you have a collection of one or more sound files that you wrap inside a Panda multifile (for instance with the multify tool), and you have the string data that represents the contents of that multifile, then you can “mount” the multifile to the vfs using a command sequence like this:

stream = StringStream(mf_data)
mf = Multifile()
mf.openRead(stream)
vfs = VirtualFileSystem.getGlobalPtr()
vfs.mount(mf, '/mf', 0)
sound = loader.loadSfx('/mf/file.ogg')

as described in the manual.

If you are using Panda3D 1.8, you can also read files from strings without first wrapping them in a multifile, by mounting a “ramdisk”. Put “vfs-mount-ramdisk /ramdisk” in your Config.prc file, and then you can write code like this:

from direct.stdpy.file import open
open('/ramdisk/file.ogg', 'wb').write(ogg_data)
sound = loader.loadSfx('/ramdisk/file.ogg')

David

Nice.
Which do you think would be faster?
Also is there anything I should look after when using “ramdisks”?

EDIT: there seems to be some issues when using ramdisks.
First I have to set it in the original Config.prc file, if I try to create it in code

loadPrcFileData('', 'vfs-mount-ramdisk /ramdisk')

then I will get errors about not being able to write to the ramdisks.
And also an error:

:audio(error): createSound(/ramdisk/test.ogg): Unsupported file or audio format.

This is all with todays buildbot version for windows.

Honestly, speed isn’t really going to be an issue here. I’d expect the multifile or ramdisk option would be about the same speed as each other, and also about the same speed as actually writing the file to disk. (In the 90’s, writing a file to disk was really slow and something to be avoided at all costs. In the 21st century, modern operating systems do a pretty good job of caching disk operations correctly, so that writing a file to disk and reading it again is entirely an in-memory operation anyway, meaning there’s no real performance cost to writing to disk.)

But one big advantage to the in-memory approach is that there’s no danger of accidentally leaving temporary files around after the program exits. On the other hand, there is a danger of accidentally filling up all your available RAM with old, unneeded files.

I don’t see the issues you report with using ramdisk; it works fine for me. Tell me more about the error message about not being able to write ramdisks.

David

Hm, how to “delete” (clear) a ramdisk?

One issue is I can’t create a ramdisk in code

loadPrcFileData('', 'vfs-mount-ramdisk /ramdisk')

only in a prc file.

The other issue is the error I get above.
I’m pretty sure my string is a valid ogg. I tried saving it to a real file and then opening it.

One of the reasons I dont want to save it to disk is the one you meantioned. I want them to be deleted after exiting the app. The other is speed really is an issue when you have few hundred files which need to be saved and read again together.

To create a ramdisk in code, use the the lines:

vfs = VirtualFileSystem.getGlobalPtr()
vfs.mount(VirtualFileMountRamdisk(), '/ramdisk', 0)

To delete a ramdisk file, use:

vfs.deleteFile('/ramdisk/file.ogg')

The file contents should match what you wrote. Are you sure you wrote them with the ‘wb’ flag, and not just ‘w’, so that there is no end-of-line conversion? You can try reading the file again (opening the file with ‘rb’) to confirm that the file data is the same data you wrote to it.

David

Hm

from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.stdpy.file import open

vfs = VirtualFileSystem.getGlobalPtr()
vfs.mount(VirtualFileMountRamdisk(), '/ramdisk', 0)

ogg_data = open('test.ogg', 'rb').read()
open('/ramdisk/file.ogg', 'wb').write(ogg_data)
sound = loader.loadSfx('/ramdisk/file.ogg')
sound.play()

run()
DirectStart: Starting the game.
Known pipe types:
  wglGraphicsPipe
(all display modules loaded.)
:movies(error): Could not open /ramdisk/file.ogg
:audio(error): Cannot open file: /ramdisk/file.ogg
:audio(error): Could not open audio /ramdis/file.ogg

Sorry, I wasn’t clear enough. I don’t want to delete the ramdisk, I want to delete the data I write() to it, so I can write another one later.

from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.stdpy.file import open

vfs = VirtualFileSystem.getGlobalPtr()
vfs.mount(VirtualFileMountRamdisk(), '/ramdisk', 0)

ogg_data = open('test.ogg', 'rb').read()
open('/ramdisk/file.ogg', 'wb').write(ogg_data)
sound = loader.loadSfx('/ramdisk/file.ogg')
sound.play()

run()

Hmm, the above code works perfectly well when I try it. I am able to hear my sound file play. I am currently on a Mac OSX computer, and don’t have access to a Windows computer; I can try it on Windows tomorrow. Which audio library are you using–OpenAL or FMod? And if you replace “/ramdisk/file.ogg” with just “file.ogg” in the above, it works correctly?

Right, vfs.removeFile() will remove the data you wrote to it, under that filename. It will also delete a regular file if you give it the name of an on-disk file. It works the same as os.unlink().

The ramdisk is just a virtual disk structure; you can write and read to it using standard commands as if you are writing and reading to disk files. You can also simply rewrite an existing file with a new open() command without bothering to remove it first.

David

I am using Windows 7 x64, trying both fmod and openal.

And if you replace "/ramdisk/file.ogg" with just "file.ogg" in the above, it works correctly? 

I think you mean replacing it with “test.ogg”? Yes, audios are played properly in the game.
I don’t think it has anything to do with audio engines, I will get the same error when trying to load a texture from ramdisk.

I think it’s either a bug in the ramdisk code or Windows expects you to implement it differently.

No, I mean “file.ogg”. I want you to read test.ogg, then write the contents of test.ogg to a local file called file.ogg, and see if it then reads it correctly.

The ramdisk code isn’t operating-system dependent, so an operating-system-specific bug here is very surprising. Plus I originally developed it on Windows anyway. The nature of your problems is baffling.

David

Hm, like this?

from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.stdpy.file import open

ogg_data = open('test.ogg', 'rb').read()

export = open('file.ogg', 'wb')
export.write(ogg_data)

sound = loader.loadSfx('file.ogg')
sound.play()

run()

Yes it works, so there probably isn’t something wrong in direct.stdpy.file.

When was this feature added? Is it possible the buildbot from yesterday didn’t pick it up? maybe the functions were there but empty or something, i don’t know.

export = open('file.ogg', 'wb')
export.write(ogg_data)

sound = loader.loadSfx('file.ogg')
sound.play() 

Interestingly, this code isn’t correct, unlike the first code segment you showed above, because it doesn’t close export between writing ogg_data and loading the sound. So there’s a possibility that the output file hasn’t been fully written yet by the time you try to read it. The correct way to do this would be to insert a call to close:

export = open('file.ogg', 'wb')
export.write(ogg_data)
export.close()

sound = loader.loadSfx('file.ogg')
sound.play() 

The first clip, above, doesn’t suffer from this problem because it doesn’t keep a file handle variable in scope, so the file will be implicitly closed immediately.

Still, this probably isn’t your problem, unless your ogg file is very small.

Naww, it’s been there for weeks. Let me download the Windows buildbot version and try it from there myself.

David

Yeah you’re right, I forgot to close() it when testing, but I usually don’t get any error when I do that which is bad because it’s become a habit for me to forget it.

OK, I found the problem. It did turn out to be a Windows-only problem, but not in a way that I would have thought–it was a weird bug in the MSVS C++ runtime library. I’ve checked in a workaround for it, should be picked up by the next buildbot.

My apologies for the trouble. Thanks for helping me track it down!

David

Great. I’ll try the new buildbot and let you know if it worked here.
EDIT: I get the same error, but ti’s possible nov 7 buildbot version didn’t pick it up.

No, it wouldn’t have, because the Nov 7 buildbot ran on the morning of Nov 7, and I committed this fix in the evening of Nov 7 (UTC). The Nov 8 buildbot would have picked it up, but because of an unrelated mistake, it didn’t complete the build. I’ve just cued up another build attempt for the win32 build, so if this one completes it should be good.

Sorry about that.

David

Yeah, the latest one has it.

All my sounds seems o load an play properly now,
…except these two:
mediafire.com/?hgt0b2ybc45fyej

They are the smallest ones, I suspect that has something to do with it.
I get this error:

:audio(error): createSound(/ramdisk/se101.ogg): Unsupported file or audio format
.
:audio(error): createSound(/ramdisk/se100.ogg): Unsupported file or audio format
.

BTW, I called close() for each

Not sure if you noticed the edit. I get that error with some files.

Thanks! Once again, I don’t have a problem with these files on my Mac. I’ll try it again on Windows the next chance I get.

David

These audio files are very small, the smallest I have in the game. I think that’s a good hint in figuring out the problem.