Joal tutorial
Address: http://jogamp.org/joal-demos/www/devmaster/lesson8.html
Original article athomas Goldberg
3-way slate brick
Retained the above information.
This is the last section of the joal tutorial series, Study Notes: http://blog.csdn.net/shuzhe66/article/details/40583771
After reading this article, I suggest you refer to the study notes. There are many questions in this section.
Lesson 8 oggvorbis format stream
This article is the joal version of The openal tutorial for devmaster.net (http://devmaster.net. Originally translated in C language by jessemaurais
"The software is copyrighted by Tor-einarjarnbjo Based on the J-Ogg library developed by http://www.j-ogg.de."
Introduction to the oggvorbis format
Have you heard of Ogg? It is not just a fun sound format name. It can be regarded as the largest event in the audio compression industry since the emergence of MP3 format (also a common music format. Maybe one day it will replace MP3 and become the mainstream standard for compressing audio. Is it really better than MP3? This question is hard to answer and has aroused great controversy in some communities. There are so many arguments about the trade-off between compression ratio and sound quality that we cannot browse through the whole article. I personally do not share any opinions on which one is better. I think there are disputes over the two compression formats and it is not worth mentioning. But for me, the reality is that Ogg is copyrighted for free (not MP3), which is favored. MP3 copyright fee is definitely not a good choice for wealthy developers, but it is not a good choice for you to spend a large amount of money to complete the project independently using limited resources and leisure time, ogg may be the answer you pray.
Design your own oggvorbis format stream API
Now you don't have to worry about it. Let's take a look at the Code:
This tutorial is written in Java. It has two main classes: oggdecoder and oggstreamer. The oggdecoder class is a packaging of the J-Ogg library. It is used to decode the oggvorbis stream. This tutorial will not describe it too much. Oggstreamer is the main class that uses openal to process most Ogg streams. I wrote it below:
// The block size is the amount of data we want to read from the stream each time. Private Static int buffer_size = 4096*8; // Number of buffers required in the audio pipeline Private Static int num_buffers = 2;
'Buffer _ size' defines a block used to hold data read from each stream. You will find that a large buffer area (somewhat empirical) can always improve the audio quality, because the number of data reads decreases accordingly, which avoids sudden interruptions of playing and distortion of sound. Of course, the buffer zone consumes more memory, so that the existence of the stream will become meaningless. I think 4096 is the minimum possible size, so we do not recommend using a smaller buffer. I tried it and it will produce a lot of noise. [For details about the buffer size and read count, refer to the notes for more details.
So why are we so entangled in the stream? Why not load the entire file into a buffer for playback? This is a good question. To put it simply, it is because there is too much audio data, even if the real Ogg file is not very large (most of them are in 1 ~ 3 MB) But you must know that it is a compressed audio data and cannot be directly used for playback. Before loading the buffer, you must decompress and format it into a format that can be recognized by openal, this is why we use the stream.
/*** Initialize and play the main loop of the stream */Public Boolean playstream (){...} /*** open the Ogg stream and initialize openal */Public Boolean open () {...} Based on the stream attributes (){...} /*** process of clearing openal */Public void release (){...} /*** play the Ogg stream */Public Boolean playback (){...} /*** check whether the video is currently playing */Public Boolean playing (){...} /*** if needed, read the next part of the stream into the buffer */Public Boolean Update (){...} /*** re-load the buffer (read into the next block) */Public Boolean stream (INT buffer ){...} /*** clear the queue */protected void empty (){...}
The above is the basis of our Ogg format stream API. The public method declared in this part is all required to play the specified Ogg, and the protected method is more like an internal process. I will not explain every method any more. I believe my comments will help you understand what they are.
// Buffer for storing sound data. two private int [] buffers = new int [num_buffers] by default (front buffer/back buffer); // the sound source private int [] source = new int [1];
The first thing I want to explain is that we declare two buffers for the stream instead of declaring one as we used to play wav. This is very important, to understand this, first think about how dual buffering works under OpenGL/DirectX. The front buffer is always displayed on the screen, the buffer is being drawn, and then the two are exchanged, and the buffer is changed to the front buffer to display the content while the latter is vice versa. The same principle applies here. The first buffer is playing while the other is waiting for playback. When the first buffer is playing, the latter starts playing. At this time, the first buffer zone reloads data from the stream and takes over after playing the buffer zone. Is it hard to understand? I will further explain these issues.
public boolean open() {oggDecoder = new OggDecoder(url); if (!oggDecoder.initialize()) { System.err.println("Error initializing ogg stream..."); return false; } if (oggDecoder.numChannels() == 1) format = AL.AL_FORMAT_MONO16;else format = AL.AL_FORMAT_STEREO16; rate = oggDecoder.sampleRate();...}
Here, a decoder is created for the Ogg file, which is initialized and obtained from the file. We obtain the enumerated values in openal based on the number of channels in Ogg and record the sampling rate.
public boolean open() {... al.alGenBuffers(NUM_BUFFERS, buffers, 0); check(); al.alGenSources(1, source, 0); check();al.alSourcefv(source[0], AL.AL_POSITION , sourcePos, 0);al.alSourcefv(source[0], AL.AL_VELOCITY , sourceVel, 0);al.alSourcefv(source[0], AL.AL_DIRECTION, sourceDir, 0); al.alSourcef(source[0], AL.AL_ROLLOFF_FACTOR, 0.0f ); al.alSourcei(source[0], AL.AL_SOURCE_RELATIVE, AL.AL_TRUE);... }
You have seen most of the code here before. We have set a bunch of default values, such as location, speed, and direction. But what is the attenuation factor? It is related to attenuation. I will discuss attenuation in detail in other articles later. I don't need to know much about it yet, but I 'd like to talk about it in general. The attenuation coefficient determines the sound attenuation distance. If it is set to 0, the corresponding function is disabled. This means that the audience can hear the sound no matter how far the audience is from the Ogg sound source, this is also true for sound sources.
public void release() {al.alSourceStop(source[0]);empty();for (int i = 0; i < NUM_BUFFERS; i++) { al.alDeleteSources(i, source, 0); check();} }
We use this method to clean up. We stop the sound source, clear any buffer in the queue, and then destroy our objects.
public boolean playback() {if (playing()) return true; for (int i = 0; i < NUM_BUFFERS; i++) { if (!stream(buffers[i]))return false;} al.alSourceQueueBuffers(source[0], NUM_BUFFERS, buffers, 0);al.alSourcePlay(source[0]); return true; }
Calling this function will start playing the Ogg. If the Ogg is playing, there is no need to do it again. We must also use the first group of data to initialize the buffer, then add it to the queue and tell the sound source to play them. This is the first time we use alsourcequeuebuffers. In general, what it does is to give the sound source multiple buffers, which will be played in order. I will soon explain the problem with the sound source queue. Remember this: if the sound source needs to be played in the stream format, do not use alsourcei to bind the buffer, but use alsourcequeuebuffers.
public boolean playing() {int[] state = new int[1]; al.alGetSourcei(source[0], AL.AL_SOURCE_STATE, state, 0); return (state[0] == AL.AL_PLAYING); }
This simplifies the detection of sound source states.
public boolean update() {int[] processed = new int[1];boolean active = true;al.alGetSourcei(source[0], AL.AL_BUFFERS_PROCESSED, processed, 0);while (processed[0] > 0){ int[] buffer = new int[1]; al.alSourceUnqueueBuffers(source[0], 1, buffer, 0); check(); active = stream(buffer[0]); al.alSourceQueueBuffers(source[0], 1, buffer, 0); check(); processed[0]--;}return active; }
In short, this is the way the queue works: there is a buffer table, when you remove the buffer from the queue, it leaves from the header. When you add a buffer to the queue, It is pushed to the end of the table. That's it, isn't it easy?
This is one of the two most important methods in this class. What we do here is to check whether a buffer zone has been played. If there is, we remove it from the queue, reload them using the data in the stream, and finally press it to the end of the queue to play them again. Ideally, the listener won't notice what we are doing. It sounds like a continuous audio chain. The stream method tells us whether the stream has been played, and the corresponding mark will be returned at the end of the function.
public boolean stream(int buffer) {byte[] pcm = new byte[BUFFER_SIZE];int size = 0;try { if ((size = oggDecoder.read(pcm)) <= 0)return false;} catch (Exception e) { e.printStackTrace(); return false;}ByteBuffer data = ByteBuffer.wrap(pcm, 0, size);al.alBufferData(buffer, format, data, size, rate);check();return true; }
This is another important method for this class. Here we use the Ogg bit stream to fill the buffer. It is difficult to control because it cannot be elaborated using top-down methods. Oggdecoder. read is the same as what you want to do. It reads data from the Ogg bit stream, and the J-Ogg library is responsible for stream decoding. We don't have to worry about this. This method uses the Bit Array as its parameter and decodes it based on its size.
The Return Value of the oggdecoder. Read method expresses many things. If it corresponds to a positive number, it indicates the actual data size to be read. This is important because the read method may not read the data size of the entire buffer zone (which often occurs at the end of the file ), in most cases, the result is the buffer size buffer_size. If the returned value of read is negative, it indicates that an error is encountered in the bit stream. If the return value is exactly 0, it indicates that there are no more parts to be played in the file.
The last part is the call to albufferdata, which fills in the data we read from the Ogg file using the read method to the buffer corresponding to the specified buffer ID, the format and bit rate parameters obtained during initialization are used here.
protected void empty() {int[] queued = new int[1];al.alGetSourcei(source[0], AL.AL_BUFFERS_QUEUED, queued, 0);while (queued[0] > 0){ int[] buffer = new int[1]; al.alSourceUnqueueBuffers(source[0], 1, buffer, 0); check(); queued[0]--;}oggDecoder = null; }
This method removes any buffer waiting in the sound source queue.
protected void check() { if (al.alGetError() != AL.AL_NO_ERROR) throw new ALException("OpenAL error raised..."); }
The error detection is simplified here.
Create your own oggvorbis player
If you follow me to learn this, you must have expectations for applying what you have learned to your actual work. Don't worry, we are about to do it. All we need to do is to use the recently designed class to play the Ogg file. From now on, the process has become relatively simple. We have completed the most difficult part. I don't think you will use this in the loop of the game, but I will recall these things during design.
All of the people below will refer to [the original article uses no-brainer literal translation as "Brainless person", which means that all people without brains will, the extension here is simple, and anyone will -- Annotation]
public boolean playstream() { if (!open()) return false; oggDecoder.dump(); if (!playback()) return false; while (update()) { if (playing()) continue; if (!playback()) return false; } return true; }
The above program opens the stream, obtains some stream information, and continuously loops when the update method returns true, and update returns true only when it successfully reads and plays the audio stream. In the loop, we will confirm that the Ogg is playing.
Questions you may ask
Can I create more than one buffer for the stream format?
In short, yes. At the same time, multiple buffers can be in the sound source queue. In this way, you will also get better results. As I have said before, if there are only two buffers in the queue and the CPU doesn't work (or the system runs as a machine ), the sound source may stop playing the video before decoding the next-class block. Using three to four buffers in a queue will lead to higher reliability when update is missed.
How often can I call update?
It depends on many things. If you want a quick response, I will tell you that the call frequency should be as high as possible, but this is sometimes not necessary. You only need to call it before the buffer in the sound source play queue. The maximum impact on this frequency value is the buffer size and the number of buffer zones allowed in the queue. Obviously, if you have a lot of data to prepare for playback, the update call frequency obviously does not need that much.
Is it safe to obtain multiple Ogg streams at the same time?
Of course there is no problem. I have not conducted any extreme tests, but I have not seen why it doesn't work. Generally, you won't have a lot of streams. You may have a character conversation that plays some background music or occasionally appears in the game, but most of the sound effects are so small that we don't need to use stream formats. Most of your sound sources have only one buffer zone bound to them.
What does the Ogg name mean?
"Ogg" is the container format for audios, videos, and metadata of xiph.org. "Vorbis" is the name of the specific audio compression plan designed in Ogg. So what does this name mean ...... This is hard to explain. I think they involve some strange relationships with Terry Pratchett's article.
Joal tutorial Lesson 8 oggvorbis format stream