Hooray! If you're reading this, I'll assume you've successfully completed the most frustrating part of dealing with a third party API—installing and configuring the libraries. Now it's time to start looking at some Ogg Vorbis code.
Using the Vorbisfile API
The vorbisfile API is the simplest Ogg Vorbis API available. It allows you to easily read Ogg Vorbis files and convert them into uncompressed PCM streams. MSITStore:C:\Downloads\游戏音频程序设计-Beginning.Game.Audio.Programming\Beginning%20Game%20Audio%20Programming\Premier.Press.Beginning.Game.Audio.Programming.eBook-LiB.chm::/6916final/LiB0046.html#ch08fig01" target="_blank" >Figure 8.1 shows how to use the API. There are other APIs available (check out the Ogg Vorbis docs), but for this chapter you'll be using the Vorbisfile API exclusively.
The API is structured in such a way as to resemble the process of reading a normal file. To work with a normal file, you open it, read from it, and then close it when you're finished. Similarly, before you can do anything else with an Ogg Vorbis file, you have to open it using the ov_open API call. This call takes as input a handle to a file that you've already opened with fopen, and the address of a OggVorbis_File structure it should fill up.
Once you've opened an Ogg Vorbis file, you can do a few different things with it. You can get some information about it by calling the ov_info API function. This takes as input a pointer to an OggVorbis_File structure (the same one you got back from ov_open), and the address of a vorbis_info structure it should fill up. The vorbis_info structure, declared in vorbis/codec.h, contains the version of the file, the number of channels, its sample rate, and possibly its bit rate (depending on if the file's bit rate changes or not).
Other useful API functions include ov_pcm_total, which, if it can, gives you the total number of samples, and ov_comment, which gives you the user comments in the file (artist, album, title, and so on).
However, the most useful API function is ov_read. This is what decompresses the file into a PCM stream. The ov_read function takes as input a pointer to an OggVorbis_File structure, a buffer of bytes, the length of that buffer (along with some parameters that tell it if you want big-endian or little-endian output), the size of your platform's word, and whether you want signed or unsigned data.
The last parameter to ov_read is a pointer to an int denoting the current logical bitstream. For simple decoding, what you want to do here is just set an int to zero, and pass the address of that int to all ov_read calls. This frees you from having to worry about logical bitstreams; however, if you're curious, check out the Ogg Vorbis documentation.
The ov_read function may not completely fill up the byte buffer you give it. Thus, it's important to check the return value, which tells you how many bytes you got back. If the return value is zero, you've hit the end of the Ogg Vorbis stream.
Once you're all done reading the file, you close it by using the ov_clear API call. Note that this also closes the FILE pointer; there's no need to call fclose yourself.
Integrating Ogg Vorbis into the Audio Engine
Now that the libraries are installed and you know how the APIs work, you can start slinging some code around. In this section, you'll learn how to load an Ogg Vorbis file into a DirectMusic segment. This sounds tough, but it really isn't. All you need to do is decompress the Ogg Vorbis file into a PCM stream (a.k.a. uncompressed stream), wrap that PCM stream in a wave file header, and pass it off to DirectMusic's Loader for loading into a segment.
You have all of the pieces of the puzzle. One of the Ogg Vorbis example programs (vorbisfile_example.c) illustrates how to use the vorbisfile library to convert an Ogg Vorbis file into a PCM stream, and thanks to MSITStore:C:\Downloads\游戏音频程序设计-Beginning.Game.Audio.Programming\Beginning%20Game%20Audio%20Programming\Premier.Press.Beginning.Game.Audio.Programming.eBook-LiB.chm::/6916final/LiB0020.html#148" target="_blank" >Chapter 4, "MSITStore:C:\Downloads\游戏音频程序设计-Beginning.Game.Audio.Programming\Beginning%20Game%20Audio%20Programming\Premier.Press.Beginning.Game.Audio.Programming.eBook-LiB.chm::/6916final/LiB0020.html#148" target="_blank" >Loading WAV Files," you've already written a CWAVFile class that can create a WAV file from a chunk of uncompressed data. Things are looking good!
I added a new method to CAudioManager, called LoadOggVorbis. Here's the source code:
CSoundPtr CAudioManager:oadOggVorbis(std::string filename)
{
std::vector<unsigned char> pcmdata;
// this code is based on vorbisfile_example.c,
// included in the ogg distribution.
char pcmout[4096]; // ogg decoding buffer
OggVorbis_File vf;
int eof=0;
int current_section;
FILE *f = fopen(filename.c_str(), "rb");
if (f == NULL) { Throw("could not open file."); }
if(ov_open(f, &vf, NULL, 0) < 0) {
Throw("Input does not appear to be an Ogg bitstream.");
}
/* Throw the comments plus a few lines about the bitstream we're
decoding */
vorbis_info *vi=ov_info(&vf,-1);
// resize the pcmdata vector to the size of the ogg file
// two bytes per channel per sample
int samples = ov_pcm_total(&vf,-1);
int channels = vi->channels;
int samplerate = vi->rate;
if (samples > 0) {
pcmdata.reserve(channels*2*samples);
}
while(!eof){
long ret=ov_read(&vf,pcmout,sizeof(pcmout),0,2,1,¤t_section);
if (ret == 0) {
/* EOF */
eof=1;
} else if (ret < 0) {
/* error in the stream. Not a problem, just reporting it in
case we (the app) cares. In this case, we don't. */
} else {
/* we don't bother dealing with sample rate changes, etc, but
you'll have to*/
// transfer the data out of the ogg buffer and into ours.
for (int q=0; q < ret; q++) {
pcmdata.push_back(pcmout[q]);
}
}
}
/* cleanup */
ov_clear(&vf);
// now that we have the PCM data in memory, convert it to a WAV file
// and handoff to DirectMusic for loading into a segment.
CWAVFile wavfile;
wavfile.m_AudioFormat = WAVE_FORMAT_PCM;
wavfile.m_BitsPerSample = 16;
// 16 bits / 8 bits per byte * channels
wavfile.m_BlockAlign = 2*channels;
wavfile.m_ByteRate = samplerate*channels*2;
wavfile.m_NumberOfChannels = channels;
wavfile.m_SampleRate = samplerate;
wavfile.SetData(pcmdata.begin(), pcmdata.size());
return(LoadSound(wavfile));
}
Most of this source code is a direct cut and paste from vorbisfile_example.c. The vorbisfile_example.c reads an Ogg Vorbis stream from stdin and writes an uncompressed PCM stream to stdout; I've modified it so that it reads an Ogg Vorbis stream from a FILE pointer and writes it into a vector of unsigned chars.
Note that this code knows the output wave is going to be 16 bits, because it specified a 16-bit word size to the ov_read function by passing 2 as the fifth parameter. If you wanted an 8-bit wave file, you'd pass 1 instead.
Once the PCM stream is in the pcmdata vector, the code gives it to a CWAVFile instance (wavfile), along with the details of its format (number of channels, sample rate, and so on). Once wavfile is all put together, the code calls the LoadSound overload you learned about in MSITStore:C:\Downloads\游戏音频程序设计-Beginning.Game.Audio.Programming\Beginning%20Game%20Audio%20Programming\Premier.Press.Beginning.Game.Audio.Programming.eBook-LiB.chm::/6916final/LiB0020.html#148" target="_blank" >Chapter 4 to read the wave file class and construct a DirectMusic segment from it. Nothing to it!