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

Go Back   DevMaster.net Forums > Site Discussions > Articles Discussion
User Name
Password
Register FAQ Members List Search Today's Posts Mark Forums Read

Reply
 
Thread Tools Search this Thread Display Modes
Old 02-05-2004, 11:04 PM   #1
DmEditor
DevMaster Editor
 
Join Date: Jan 2005
Posts: 54
Default

Title: Loading OggVorbis Files From Memory
Author: Spree Tree
Description: Based on Lesson 8 of the OpenAL series, this tutorial demonstrates how to load an OggVorbis from files into memory. Sample source code is included.
DmEditor is offline   Reply With Quote
Old 06-08-2005, 10:30 PM   #2
CasualT
New Member
 
Join Date: Jun 2005
Posts: 2
Default

Nifty tutorial. I know that the Ogg guys are just trying to be helpful with their API based on FILE *. However, my system (like I'm sure many others) uses a file manager. I haven't quite figured out how I'm going to handle the streaming yet, but I'm glad you covered the aspects of dealing with the Ogg files once you get them loaded in memory.
CasualT is offline   Reply With Quote
Old 10-29-2005, 02:58 AM   #3
A.Russell
New Member
 
Join Date: Aug 2004
Posts: 15
Default Re: Loading OggVorbis Files From Memory

Me again, sure you don't want that fiver Spree Tree?

I've been trying to load a whole ogg into a buffer to play rather than streaming it. This is useful for pre-loaded sound effects.

I don't get any errors, but it doesn't make any sound either:

Quote:
Precondition: Needs a string for the fie path
Postcondition: returns a handle for OpenAL to play.

ALuint OpenALWrap::LoadALData(std::string song_path)
{
//Load an ogg file
if(NULL == (oggFile = fopen(song_path.c_str(), "rb")))
throw string("Could not open Ogg file.");

//Open it
song_handle = ov_open(oggFile, &oggData, NULL, 0);

//Make a buffer for it -I had some trouble with this
//It should be the same size as the file,
//but I couldn't do that. So for the time being I've
//just made a really large buffer

int filesize = 4096*100; //ov_pcm_total(&oggData,-1);<-I thought that would return the size of the file

char pcm[4096*100]; //change this to file size


ALuint buffer;
alGenBuffers(1, &buffer);


int section;
int result;

//***The problem seems to be with ov_read
//***result is always 256 regardless of the file
//***I think it should be the number of bytes in the file

result = ov_read(&oggData, pcm, filesize, 0, 2, 1, &section);

if(result > 0)
cout << "\n" << song_path << " was loaded successfully";
else
if(result < 0)
cout << errorString(result);


vorbisInfo = ov_info(&oggStream, -1);

if(vorbisInfo->channels == 1)
format = AL_FORMAT_MONO16;
else
format = AL_FORMAT_STEREO16;


alBufferData(buffer, format, &pcm[0], result, vorbisInfo->rate);
check();

KillALLoadedData();

return buffer;
}

Last edited by A.Russell : 10-29-2005 at 03:04 AM.
A.Russell is offline   Reply With Quote
Old 11-03-2005, 03:16 AM   #4
A.Russell
New Member
 
Join Date: Aug 2004
Posts: 15
Default Re: Loading OggVorbis Files From Memory

Another try at this.

I'm trying to load one buffer with athe entire contents of an .ogg file so that it can be played as a pre-loaded sound effect -rather than streamed like in the tutorial.

I have altered the earlier tutorial for playing .wav files.

Code:
ALuint OpenALWrap::GetLoadedALBuffer(std::string cp_wave_fname) { int count = 0; // 'count' will be an index to the buffer list. ALuint buffer; // Buffer id for the loaded buffer. // Iterate through each file path in the list. for(vector<string>::iterator iter = LoadedFiles.begin(); iter != LoadedFiles.end(); ++iter, count++) { // If this file path matches one we have loaded already, return the buffer id for it. if(*iter == cp_wave_fname) return Buffers[count]; } //******This is where I should load the buffer // If we have made it this far then this file is new and we will create a buffer for it. // buffer = LoadALBuffer(cp_wave_fname); //change this for loading a .ogg file instead


the next bit is the same as in the tutorial. Here the .ogg is loaded:

Code:
if(NULL == (oggFile = fopen(cp_wave_fname.c_str(), "rb"))) throw string("Could not open Ogg file."); // Find out how big the file is sizeOfFile = 0; while (!feof(oggFile)) { tempChar = getc(oggFile); sizeOfFile++; } // Save the data into memory oggMemoryFile.dataPtr = new char[sizeOfFile]; rewind(oggFile); tempArray = 0; while (!feof(oggFile)) { oggMemoryFile.dataPtr[tempArray] = getc(oggFile); tempArray++; } // Close the ogg file fclose(oggFile); // Save the data in the ogg memory file because we need this when we are actually reading in the data // We havnt read anything yet oggMemoryFile.dataRead = 0; // Save the size so we know how much we need to read oggMemoryFile.dataSize = sizeOfFile; // Now we have our file in memory (how ever it got there!), we need to let the vorbis libs know how to read it // To do this, we provide callback functions that enable us to do the reading. the Vorbis libs just want the result // of the read. They dont actually do it themselves // Save the function pointersof our read files... vorbisCallbacks.read_func = VorbisRead; vorbisCallbacks.close_func = VorbisClose; vorbisCallbacks.seek_func = VorbisSeek; vorbisCallbacks.tell_func = VorbisTell;

Now, this is where it gets confusing:

Code:
// Open the file from memory. We need to pass it a pointer to our data (in this case our SOggFile structure), // a pointer to our ogg stream (which the vorbis libs will fill up for us), and our callbacks if (ov_open_callbacks(&oggMemoryFile, &oggData, NULL, 0, vorbisCallbacks) != 0) throw string("Could not read Ogg file from memory");

I take it the ogg file is loaded and decompressed into a location pointed to by &oggData???

Now I check the file:
Code:
vorbisInfo = ov_info(&oggData, -1); vorbisComment = ov_comment(&oggData, -1); if(vorbisInfo->channels == 1) format = AL_FORMAT_MONO16; else format = AL_FORMAT_STEREO16; printf("\nFile: %s \n", cp_wave_fname.c_str()); display(); printf("\nsize of file: %d", sizeOfFile);

Looks okay.

And here is where I think the problem is:

Code:
alBufferData(buffer, format, &oggData, sizeOfFile, vorbisInfo->rate); check();

Is oggData the "pcm" that is supposed to be loaded into the buffer. If not, then what is? I am completely lost here. What exactly is the parameter I am supposed to feed alBufferData?

The function ends by returning a pointer to my screwed up buffer:

Code:
// Add this new buffer to the list, and register that this file has been loaded already. Buffers.push_back(buffer); LoadedFiles.push_back(cp_wave_fname); return buffer;
A.Russell is offline   Reply With Quote
Old 11-03-2005, 04:24 AM   #5
SpreeTree
Valued Member
 
SpreeTree's Avatar
 
Join Date: Jan 2004
Location: England
Posts: 265
Default Re: Loading OggVorbis Files From Memory

I will admit to hating the way in which ogg vorbis and OpenAL work together. I have written countless ogg stream classes for OpenAL applications, and I have yet to get one that feels right. Anyway, thats besides the point

I'll have a look through your code and comment on things as I see them. I don't have access to my code base at the moment, so you can try any changes I suggest and see how that goes.

One thing I must say is that I don't think it is simpler to pre-load the ogg buffer format. The tutorial assumes that all buffers will be of x length, whereas, you will not know the size of the buffer until after the file has been decoded.

There is a simpler way to read the contents of a file into memory. I don't know why I did it that way in the tutorial, maybe for clarity purpose but anyway :S

Try something like the following (there could be syntax errors here)
Code:
FILE* file; int bytesRead; int fileSize; char* fileInMem; // Get the file size by just going to the end fseek(_file, 0, SEEK_END); fileSize= ftell(_file); fseek(_file, 0, SEEK_SET); // Create the memory fileInMem = new char[fileSize]; // Read the file in one go bytesRead = fread(fileInMem, 1, fileSize, file));

Regarding ov_open_callbacks(...)

This just opens the file ready to be decoded and nothing more.

The first param if the memory location of the file to play, which we have loaded above. The second param doesn't have the decoded sample, as that would make streaming pointless. It collects all the information about the ogg file you have just loaded (I assume it just reads the header). For example

Code:
// Get the information on the vorbis file streamingSampleData.vorbisInfo = ov_info(&streamingSampleData.oggStream, -1); streamingSampleData.vorbisComment = ov_comment(&streamingSampleData.oggStream, -1); // Check the format if(streamingSampleData.vorbisInfo->channels == 1) streamingSampleData.oggFormat = AL_FORMAT_MONO16; else streamingSampleData.oggFormat = AL_FORMAT_STEREO16; streamingSampleData.oggFrequency = streamingSampleData.vorbisInfo->rate; // Display the ogg information mary::g_debugStream->Printf("Ogg File Successfully Loaded: [%s]\n", GetProductName()); mary::g_debugStream->Printf("Ogg Vendor: %s\n", streamingSampleData.vorbisComment->vendor); mary::g_debugStream->Printf("Ogg Version: %d\n", streamingSampleData.vorbisInfo->version);

The actual decoding comes now.

Now that the ogg data has been loaded, we need to read it bit by bit (or in your case, the whole lot). And we do that using ov_read (which calls our read callback we specified earlier).

ov_read needs to know the format data (which is in the OggVorbis_File we opened above), the memory we will recive the decoded data to (which will be the pcm data you need), the amount of data we can read (which, at max, should be the size of your pcm data). The last params shoud be 0, 2, 1, &section); and never change (with section being a signed int).

Now in your case, you want to read the entire file. So, you would have to pass in a large pcm pointer (which is just a pointer to char) and a large amount to read (as the read will only read what it can).

When you have read this, the pcm data is then passed to alBufferData(...) and the buffer can be played as you would with a wav file.

Hope that helps some
Spree
SpreeTree is offline   Reply With Quote
Old 11-07-2005, 06:27 AM   #6
A.Russell
New Member
 
Join Date: Aug 2004
Posts: 15
Default Re: Loading OggVorbis Files From Memory

The Ogg Vorbis SDK is very frustrating.

With that off my chest, I've implimented the advice from your last post, but I am still stuck. What I have now is a program that will seem to load one of six ogg files correctly (the second to last file, so it has nothing to do with order), but will only play about half of its sound.

I've modified lesson six and made an alternate function to load ogg files instead of wavs. In the Tutorial the function that does this is LoadALBuffer, so I've made LoadALBufferOgg, and it is called just the same from GetALLoadedData.

I wonder if it has something to do with what you said in your last post:

Quote:
The tutorial assumes that all buffers will be of x length, whereas, you will not know the size of the buffer until after the file has been decoded.

I'm not sure why this is important, though. I can't see anywhere that you need to specify the size of a buffer. It simply takes as much data as it is given, doesn't it? If it happens to be BUFFER_SIZE or the more dynamic sizeOfFile, I can't see where the size of the buffer need to be entered.




Here's what I've done anyway. Perhaps you can what is wrong, or a mistake in my logic:

Code:
ALuint OpenALWrap::LoadALBufferOgg(std::string cp_wave_fname) { //define some variables FILE* tempOggFile; // file handle ALuint buffer; // Buffer id for the loaded buffer. ALuint result2; vorbis_info* MyInfo; // some formatting data vorbis_comment* MyComment; // user comments OggVorbis_File oggData; //for fully loaded sound effects //generate a buffer for OpenAL alGenBuffers(1, &buffer); if ((result2 = alGetError()) != AL_NO_ERROR) cout << "\nCouldn't generate a buffer: " << GetALErrorString(result2); //load an ogg file if(NULL == (tempOggFile = fopen(cp_wave_fname.c_str(), "rb"))) printf("\ncould not open ogg file"); //find out how big it is sizeOfFile = 0; fseek(tempOggFile, 0, SEEK_END); sizeOfFile = ftell(tempOggFile); fseek(tempOggFile, 0, SEEK_SET); // Save the data into memory oggMemoryFile.dataPtr = new char[sizeOfFile]; rewind(tempOggFile); tempArray = 0; while (!feof(tempOggFile)) { oggMemoryFile.dataPtr[tempArray] = getc(tempOggFile); tempArray++; } //save the ogg file into memory oggMemoryFile.dataRead = 0; oggMemoryFile.dataSize = sizeOfFile; vorbisCallbacks.read_func = VorbisRead; vorbisCallbacks.close_func = VorbisClose; vorbisCallbacks.seek_func = VorbisSeek; vorbisCallbacks.tell_func = VorbisTell; if (ov_open_callbacks(&oggMemoryFile, &oggData, NULL, 0, vorbisCallbacks) != 0) cout << "Could not read Ogg file from memory"; //define some variables for moving and converting the ogg file for use by OpenAL char* pcm = new char[sizeOfFile]; int size = 0; int section; int result;

This next part was in your post, but I don't know what it is supposed to do. Isn't the data for the pcm array feed in by ov_open? This will just get overwritten, won't it?

Code:
// Read the file in one go result = fread(pcm, 1, sizeOfFile, tempOggFile);

And now, for the very confusing and frustrating ov_read function. I have implimented this in the same way as in the streaming tutorial, but it is very odd. Each loop reads in a number of bytes that is a power of two, until the last loop. On the last loop it will either red right to the last byte, or it will leave one byte to go. If it leaves one last byte, then on the next loop it will return -131, unkown ogg error. Not very helpful. What is especially odd is that the only file that loads successfully is the second to last of six ogg files. There isn't anything I can see that is special about this file, except that it actually loads without an error and half of it will play.

Code:
//convert the ogg data and store it in the pcm char string while(size < sizeOfFile) { result = ov_read(&oggData, pcm + size, sizeOfFile - size, 0, 2, 1, &section); if(result > 0) { size += result; } else if(result < 0) { printf("\nbad result for %s result %d \n", cp_wave_fname.c_str(),result); cout << errorString(result); break; } else break; } //store the pcm data in the buffer we created MyInfo = ov_info(&oggData, -1); if(MyInfo->channels == 1) format = AL_FORMAT_MONO16; else format = AL_FORMAT_STEREO16; alBufferData(buffer, format, pcm, sizeOfFile, MyInfo->rate); check(); //and that's it return buffer; }


This is exactly the same as tutorial 6 except for this function, which should return a handle to a buffer with data from an ogg file instead of a wave.

The six ogg files all play perfectly well in other media players. Some of them are stereo and others mono.



Code:
One thing I must say is that I don't think it is simpler to pre-load the ogg buffer format.

My main reason for not streaming them is the pause that you get when loading a file from HDD. It is noticable in real time games. Sound effects are usually very small and may need to be repeatedly played many times.

Also, the streaming function in the tutorials doesn't work properly with small files. I haven't found out why yet, perhaps itis something that happens if the file isn't big enough to fill the two buffer?
A.Russell is offline   Reply With Quote
Old 11-07-2005, 07:01 AM   #7
SpreeTree
Valued Member
 
SpreeTree's Avatar
 
Join Date: Jan 2004
Location: England
Posts: 265
Default Re: Loading OggVorbis Files From Memory

Ok, I've had a look through your code, but I'm on someone elses machine at the mo, so what I say will be from what I can remeber... But stick with me!

First I think we need to clear up the difference between what the ogg format contains, and what the pcm data is that you want.

The ogg format contains (very) compressed audio data, which is obviously why the file is so small. When you start working with the ogg file, you will not know how compressed the audio data is. So there for, you cannot know how much memory the uncompressed PCM data will take up.

As a side note, if you converted a 3MB ogg file to a normal pcm wav file, the average size of the wav file is ~50Mb... Which is obviously memory you cannot spare!

This is where ov_open and ov_read comes in.

ov_open, as I mentioned, just reads in the ogg format header. This has nothing to do with the PCM data needed by openAL. This will allows the ogg vorbis internals to calculate the compression methods, file properties etc.

ov_read is where your problem lies. ov_read calls your ReadFromMemory function. This function gets passed the size of data to copy from the ogg source file. So you copy a chunk of still compressed data from the ogg source file into the memory ptr passed to ReadFromMemory(...).

It is then, and only then, that the ogg data is decompressed into the pcm data buffer that you passed to ov_read. Obviously, we will only know the size of the data copied to the pcm data buffer after we have decompressed it, hence ov_read returning how much data has been read. This will allow you to know how much data has been read into the pcm buffer, so next time you can point the ov_read function to read to the point pcmBufferStart+amountReadSoFar.

Then, when the amountReadSoFar is as big as the pcm buffer size, you know that you have decompressed as much as you can in this loop. This will then allow you to either:

a) play the buffer - which I think is what you want to do
b) pass another buffer - which is what you do when you stream

So what will have happened by now is that you have read compressed data from the ogg file in ReadFromMemory, and that memory has been decompressed into the pcm buffer that will be passed to Play.

Now I'll explain why you are encountering the problem you are.

Only Playing Half A Sound
This is because, as you use ov_read, you have provided a buffer that is only big enough to contain half of the sample decompressed. The buffer you have provided needs to be much bigger if you do not want to use streaming. Or you need to use ov_read on another buffer, then queue the buffers as per the tutorial.

No Sound Is Playing
Various things could be causing this, but for the sake of this, I would say it is due to the buffer being streamed to is not big enough to contain data that has sound in it! Maybe the first part of the track is silent?

I purposly have not included any source as I am worried you don't clearly understand how the ogg vorbis SDK works at the moment (don't worry about that, as you say, it isn't perfect but you will get there!). So i have just stuck to the theory of ogg vorbis for the moment.

As for your other specific questions

Quote:
Originally Posted by A.Russell
then on the next loop it will return -131, unkown ogg error.

Can't help with that at the moment as I don't have the ogg source to hand. -131 sounds a bit odd though. Are you making sure you are not over-ridding any data in your ReadFromMemory function?

Quote:
Originally Posted by A.Russell
Some of them are stereo and others mono.

FYI stereo samples cannot be played positionally. Just make sure this is not causeing you any problems with the mono samples (the source position not being set etc.)

Quote:
Originally Posted by A.Russell
My main reason for not streaming them is the pause that you get when loading a file from HDD. It is noticable in real time games.

If streaming is done correctly, there is no pause. As the buffers you stream into are generally quite small, are queued and then re-filled, the time taken to fill the buffers and assign them to a source is very small, and definatly not noticable.

Quote:
Originally Posted by A.Russell
Sound effects are usually very small and may need to be repeatedly played many times.

and

Also, the streaming function in the tutorials doesn't work properly with small files. I haven't found out why yet, perhaps itis something that happens if the file isn't big enough to fill the two buffer?

You shouldn't be using streaming for short, simple sound effects. Thats what basic wav files are for. The whole point of using ogg files is for sounds that would be too big for normal wav files. This is usually music or speech.

This is why the tutorial will not work for short effect. It is not designed for that, and shouldn't be expected to work.

I am worried you are going off on a tangent a little, and not getting a clear idea of what ogg files and (more specifically) streaming is for.

Simple, short effects - Use wavs
Music or speech - Use streaming

It might be a good idea to rethink that you are using ogg files for before you tear your hair out in anger (with OpenAL, its not that hard to go bold IMO!).

Anyway, I hope this helps some. Let me know how it goes
Spree
SpreeTree is offline   Reply With Quote
Old 11-08-2005, 04:41 AM   #8
A.Russell
New Member
 
Join Date: Aug 2004
Posts: 15
Default Re: Loading OggVorbis Files From Memory

Thanks a lot Spree Tree,

After your explanation I could get it working within a few minutes.

Now I understand what you meant by not knowing how much space would be needed for pcm data in advance. What I've done isn't very elegant, but it works well enough. I only had to change a few lines:

Code:
char* pcm = new char[sizeOfFile*20]; //not very lovely, but makes certain there is enough space for the pcm data ... int size = 0; while(size < sizeOfFile*20) { result = ov_read(&oggData, pcm + size, sizeOfFile*20 -size, 0, 2, 1, &section); if(result >0) { size += result; } .... alBufferData(buffer, format, pcm, size, MyInfo->rate);

Now we have pre-loaded ogg files for sound effects!

Quote:
FYI stereo samples cannot be played positionally. Just make sure this is not causeing you any problems with the mono samples (the source position not being set etc.)

Currently I just have everything being played relatively (AL_SOURCE_RELATIVE), though thanks for the heads up.

Quote:
If streaming is done correctly, there is no pause. As the buffers you stream into are generally quite small, are queued and then re-filled, the time taken to fill the buffers and assign them to a source is very small, and definatly not noticable.

I used a 3d engine (closed source) that employed DirectShow for streaming and DirectSound for pre-loaded effects. There certainly was a pause on that engine. It could be pretty bad on an old machine, too. That's why I thought about it.

Quote:
You shouldn't be using streaming for short, simple sound effects. Thats what basic wav files are for. The whole point of using ogg files is for sounds that would be too big for normal wav files. This is usually music or speech.


Although there is no advantage in the application, it makes a difference when distributing over the net. All those little sound effects add up. By using oggs several megs can be shaved off the final product. Think of all those poor people in countries like New Zealand on dial up!

Quote:
It might be a good idea to rethink that you are using ogg files for before you tear your hair out in anger (with OpenAL, its not that hard to go bold IMO!).

I have some very expensive hair tonics, and they seem to be working.

Thank you again for all your help!
A.Russell is offline   Reply With Quote
Old 03-11-2007, 05:38 PM   #9
rustedivan
New Member
 
Join Date: Mar 2007
Posts: 1
Default Re: Loading OggVorbis Files From Memory

I realize that this thread is probably mostly dead, but for late-comers this might be an interesting piece of information:

It is actually possible to do an exact prediction of the uncompressed Vorbis data size. The idea is that you count the number of PCM samples in the file, the number of channels in the file and the number of bitstreams in the file. This gives you the total number of PCM "frames" that will be decoded.

The size of each frame is given by the resolution parameter that you're passing to ov_read. Just multiply the result from above by the resolution parameter (typically 2) and you're done.

The number of PCM frames is readily given by ov_pcm_total().
rustedivan is offline   Reply With Quote
Old 01-20-2008, 04:44 PM   #10
jackskelyton
New Member
 
Join Date: Jan 2008
Posts: 9
Default Re: Loading OggVorbis Files From Memory

I am having a similar problem to A.Russel.... I have implemented your example almost exactly, using my own datastruct (a class called Sound that contains a pointer to an instance of another class called SFResource that holds the points to the Ogg data in memory) in the custom callback functions. I've had all kinds of problems implementing this Ogg decoder, and in an effort to simplify the problem I reduced the buffer fill to just one 32k buffer, using a sound file that's small enough to fit inside.

My Open function is word-for-word what yours is. My other code:

Code:
bool Sound::StreamOgg(ALuint buffer) { char data[BUFFER_SIZE]; int size = 0; int section; int result; while(size < BUFFER_SIZE) { if(result > 0) { size += result; } else { if (result < 0) { printf("[Sound] Error during Ogg stream decoding: %s\n", this->GetName()->GetCString()); break; } else { break; } } } alBufferData(buffer, mFormat, data, size, mVorbisInfo->rate); CheckError(); return false; }

Code:
bool Sound::PlayOgg() { // check if Ogg is loaded yet if(!mSource) { this->OpenOgg(); } StreamOgg(mBuffers[0]); alSourcei(mSource, AL_BUFFER, mBuffers[0]); alSourcePlay(mSource); return true; }

I had an earlier problem where everything seemed to work fine, but the data buffer in Sound::StreamOgg kept filling with zeros. I thought it might be a problem with my open_func callback, but I can't for the life of me see where it is. That code:

Code:
size_t VorbisRead(void* ptr, size_t byteSize, size_t sizeToRead, void* datasource) { size_t spaceToEOF; size_t actualSizeToRead; // cast "datasource" to Sound object, as that is what we pass in Sound* soundObject = (Sound*)datasource; spaceToEOF = soundObject->mSoundResource->GetSize() - soundObject->mDataRead; if ((sizeToRead * byteSize) < spaceToEOF) { actualSizeToRead = (sizeToRead * byteSize); } else { actualSizeToRead = spaceToEOF; } if(actualSizeToRead) { memcpy(ptr, (char*)soundObject->mSoundResource->GetData() + soundObject->mDataRead, actualSizeToRead); soundObject->mDataRead += actualSizeToRead; } return actualSizeToRead; }

Do you see any place where I might be going wrong?
jackskelyton is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Forum Jump


All times are GMT -7. The time now is 03:19 AM.


Powered by vBulletin
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.