| DevMaster.net |
| [[ Home | Forums | 3D Engines Database | Wiki | Articles/Tutorials | Game Dev Jobs | IRC Chat Network | Contact Us ]] |

| Loading OggVorbis Files From Memory |
|
|
|
05/02/2004
|
||
IntroductionThis tutorial is designed to follow directly from OpenAL Lesson 8: OggVorbis Streaming - Using The Source Queue by Jesse Maurais. For this reason, the code base used is almost identical to the one used in that lesson, the only changes being are in regards to how the file is loaded. Everything else is exactly the same. That's one of the real strengths of the OggVorbis libraries, you can load in the .ogg file however you please, and all the other calls are used in exactly the same way. So, on with the show. Getting The File Into MemoryThe OggVorbis file needs to be loaded into memory before we can start parsing them using the vorbis libraries. The method used in the example program is, in no uncertain terms, a true bodge. Normally, the file would have been preloaded into memory at loading time, maybe extracting it from a pak file or the like. For the sake of this tutorial, just ignore how I've gotten it into main memory. Getting Ready To Read From MemoryThe Vorbis libraries don't actually have any support for loading files from memory, they leave it up to you, only asking that you return the right data as and when they need it (This is a pretty cool method, meaning you can read the .ogg file from just about anywhere and anyhow). For this to work, they require you to provide four callback functions (if you don't know what a callback function is, get reading a book on C programming ;) The callbacks are passed using the ov_callbacks structure and they are:
These functions are expected to work in exactly the same way as the standard C IO functions (fread(...), ftell(...) etc.), and as such have the same inputs and the same return values. Once we have declared our callback functions, we store them within the ov_callback structure vorbisCallbacks.read_func = VorbisRead; vorbisCallbacks.close_func = VorbisClose; vorbisCallbacks.seek_func = VorbisSeek; vorbisCallbacks.tell_func = VorbisTell; Now that we have told the vorbis libs how we are going to perform all the different IO actions on the data, we need to actually pass the load function a pointer to our data. To do this, we need to define our own struct, as this will allow us to perform all the different IO actions we need. The struct is as follows: struct SOggFile { char* dataPtr; // Pointer to the data in memory int dataSize; // Size of the data int dataRead; // How much data we have read so far }; SOggFile oggMemoryFile; Once we have the structure we need to initialise it. Pretty simple stuff. We save the pointer to the data in dataPtr, the size of the data in dataSize, and set dataRead to 0 (as we have yet to actually read anything in). Now we just need to set the wheels in motion ov_open_callbacks(&oggMemoryFile, &oggStream, NULL, 0, vorbisCallbacks) This function informs the libraries that we want to open an .off file, but instead of using a FILE, we are going to read in the data ourselves. The only thing here that we haven't seen before is the oggStream. This is just a pointer to a OggVorbis_File, which will be set up for us as we read in the file. Reading In The Data From MemoryNow all that is left to do is actually read the data from memory. This is all done inside the callback functions that we defined earlier. Lets take a look at each on in turn... VorbisReadsize_t VorbisRead(void *ptr, // ptr to the data that the vorbis files need size_t byteSize, // how big a byte is size_t sizeToRead, // How much we can read void *datasource) /* this is a pointer to the data we passed into ov_open_callbacks (our SOggFile struct)*/ { ... } This function requires you to read in the data from memory, and place it into the ptr variable (which will be of size byteSize*sizeToRead). All we need to do in this case is to memcopy(...) our data from memory into the ptr, making sure that we do not go beyond the bounds of our data in memory. A return of 0 means that we have reached the end of the file, and we were unable to read anymore data. Otherwise, we must return the amount of data we have read. VorbisSeekint VorbisSeek(void *datasource, // this is a pointer to the data we passed into ov_open_callbacks (our SOggFile struct) ogg_int64_t offset, // offset from the point we wish to seek to int whence) // where we want to seek to { ... } This function works in the same way as fseek(...). We are given a point from which to seek (SEEK_SET, SEEK_CUR, SEEK_END), and we must set our data pointer accordingly (again making sure we do not pass past the boundary of our data). A return of -1 means that this file is not seekable (i.e. you cannot move the pointer, and as such, cannot rewind the file). This is fine if we don't want to loop the sample, but if we do, we need to be able to set the pointer back to the beginning of the file. A return of 0 is a successful call. VorbisCloseint VorbisClose(void *datasource) // this is a pointer to the data we passed into ov_open_callbacks (our SOggFile struct) { ... } This function is called when we call ov_close(...) within our code. The main use of this function is to clean up any allocations made during the opening of the file (of which there should be few, if any). You could, if you wanted, clean up the file in memory, but in most cases I assume the memory would be cleaned up elsewhere, and so this function is left empty. The return clause is irrelevant. It is assumed this function always succeeds. VorbisTelllong VorbisTell(void *datasource) // this is a pointer to the data we passed into ov_open_callbacks (our SOggFile struct) { ... } This is used to inform the libraries how much of the file we have read so far. Pretty simple stuff, just return the amount we have read. A return of -1 indicated an error, but I can't imagine when this might happen! ConclusionThere we have it. A few simple functions and the file is loaded from memory. The only thing left to do is clear memory of the file we loaded into memory in the first place, but it needs to be there through the duration of the sample (and beyond if we are looping it, or playing it again) As you've probably gathered, by using the callbacks we can load a file from absolutely anywhere, not just in memory. By giving the developers a means of controlling the how and when the data is read in, allows us to mess around with the sound as we see fit, allowing us to create simple effect without having to change the actual source sample! Included with this tutorial is a sample program that demonstrates the implementation (see link below). I hope this tutorial is of use to some of you out there, it would be good to know that my bedroom coding is always going to waste;) If you have any queries or problems, I'm sure you will be able to drop me a line, or post in the forums. Download the Linux port of this tutorial - (ported by Lee Trager) |
|
|
| © 2003-2004 DevMaster.net. All Rights Reserved. Terms of Use & Privacy Policy | Want to write for us? Click here |