Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - luiscubal

Pages: « 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 »
121
Proposals / Re: Multiple Theads, are they a possibillity?
« on: January 27, 2011, 09:46:17 am »
I believe sound(OpenAL) is already in a separated thread.

Multithreading would break compatibility with GM, although some kind of limited multithreading support might be possible in the physics subsystem(hspeed, vspeed, gravity, friction and collisions).

Multithreading the draw event might be problematic, even if no sane person would make the Draw event change variables like position, etc.

122
Ideas and Design / Re: Rooms versus Windows
« on: January 24, 2011, 01:32:56 pm »
I suppose "Pane" is a pretty good name.

123
Ideas and Design / Re: Extension API
« on: January 23, 2011, 11:39:54 am »
That would be up to Josh. I am, however, guessing that for C++ extensions at least the header files will be unprotected.

124
Ideas and Design / Re: Extension API
« on: January 22, 2011, 09:58:26 pm »
Not enough. We're talking about entire resource types.
Josh mentioned a while back that he would like paths and alarms to be entirely extensions. What you describe can not possibly implement this properly.

125
Ideas and Design / Extension API
« on: January 22, 2011, 05:34:42 pm »
I propose an extension API for ENIGMA based on the following concepts:

1. Each extension will typically have a Java/LGM part and an "execution" part(C++/ENIGMA or whatever the backend is)
3. Each execution component has a part of it that describes its function. This might be in Java, XML or any other cross-platform language that LGM can understand.

-------------------------------------

Core Extension API

Code: [Select]
LGM.requestRestart(String msg); //If the extension requires a restart
LGM.installExtension(String path, Object params); //Install "with args"
LGM.installExtension(String path); //Same as installExtension(path, null)
LGM.uninstallExtension(Extension extension);

Code: [Select]
class Extension
public String getName()
public String getDescription()
public String[] getAuthors()
public java.awt.Image getIcon()
public boolean getEnabled()
public void setEnabled(boolean enabled);
public ExtensionContent load();

Code: [Select]
interface or class ExtensionContent
public void install();
public void load(); //Extension code and resources were loaded, now handle the load logic
public void enable(); //Enable the extension

This is the barebones, now the interesting part:

Code: [Select]
void LGM.registerResourceType(ResourceType resource);
void LGM.unregisterResourceType(ResourceType resource);
GameFolder LGM.createGameFolder(String folderName, ResourceType resource, boolean canRename);
void LGM.renameGameFolder(GameFolder folder);
void LGM.deleteGameFolder(GameFolder folder);

Code: [Select]
class or interface ResourceType
public String getName();
public Image getIcon();
public Designer createDesigner(Object resource);
//Missing is the serialization API for loading/saving. I don't think I can properly design this without also designing a file format, which is not within the scope of this topic, so someone else will have to do this part.

Code: [Select]
class Designer extends JPanel
//Stuff like designer window title, etc.

Now for the "execution" part:
Code: [Select]
LGM.registerExecutionPlugin(ExecutionPlugin plugin);

Code: [Select]
class or interface BackendInfo
public String getName(); //return "enigma";
public String getLanguage(); //return "cpp";
public boolean isDebugMode();
public String getFullInfo(); //something like "enigma/cpp debug windows+opengl+openal"
//Possibly more functions

Code: [Select]
class or interface ExecutionPlugin
public String getName();
public boolean acceptsBackend(BackendInfo info); //Whether the execution plugin was made for this backend
public Script[] getScripts(); //Creates GML scripts for the plugin, useful for pure GML plugins
//The rest would be backend-specific. For instance, ENIGMA could provide a EnigmaExecutionPlugin.
//Do note that even if a plugin was not EnigmaExecutionPlugin, ENIGMA would accept it based ONLY on the acceptsBackend function, in order to allow pure GML extensions.

I am not sure how to allow pure GML scripts to handle custom resources, though.

This is just a proposal. Feel free to use, modify and/or ignore it. I release this proposal under the WTFPL.

126
Ideas and Design / Re: Rooms versus Windows
« on: January 22, 2011, 04:52:40 pm »
Pausing and focus-grabbing is weird, since not all windows would pause the game and focus-sharing...
But yes, an extension system like that could be better.
I am unsure if windows should contain instances or just Create/Destroy/Step/Draw events(perhaps with just plain GML?)...

127
Ideas and Design / Re: Rooms versus Windows
« on: January 22, 2011, 03:07:40 pm »
I'm not sure I like having windows bound to the room editor. After all, some windows tend to be common across multiple rooms, so while some of them are room-specific others transcend that concept.
Consider, for example, a message window(RPG talk), the window in this case is closer to objects than to rooms.

That said, I also think views should be separated from the room editor.

128
Ideas and Design / Re: Rooms versus Windows
« on: January 21, 2011, 07:18:48 pm »
It is entirely unrelated to this topic. As I said, I mean "Windows" as in-game GUI controls.

129
Ideas and Design / Rooms versus Panes (formerly Windows)
« on: January 21, 2011, 02:31:29 pm »
Disclaimer: This is NOT an operating system discussion.

ENIGMA uses rooms, a concept it inherits from Game Maker.
I'm going to ignore the issue of compatibility for now.

The idea behind Windows/Panes(a concept based on RPG Maker) is *NOT* to have a game with multiple windowing system windows. That'd be awful. I mean in-game windows/panes, such as dialogs and menus.
If you think about it, most games feature some sort of controls system, which I typically implement in Game Maker using the Draw event.
"Pause" is typically kind of an ugly hack, and so is keyboard management with menus.

Windows/Panes(along with focus) solve this problem by ensuring only the focused window/pane receives keyboard events(or something like having a "Global Keyboard Event" like what happens with the mouse). For RPGs, this means messages are incredibly simple to implement.
For some windows/panes, this might require some more effort, to ensure the world "stops" when e.g. pausing the game(set hspeed=0, etc.)
Still, simple compared to the situation of what we have in GM today, where I find myself reimplementing the system for some feature subset.

What do you think about this? Surely, Windows/Panes can be implemented using Rooms and Objects. But... what about doing things the other way around? Like implementing views using windows/panes.

130
Announcements / Re: Happenings
« on: January 18, 2011, 09:49:30 am »
"case default:"?

131
@Josh: Seriously? I mean, if the system is already in OpenAL and you can already mark sounds as 3D(and figure out a way to make them mono at compile time, since I heard OpenAL 3D doesn't like stereo):


Code: [Select]
void sound_3d_set_sound_position(int snd, float x, float y, float z) {
   Sound* s = GetSoundFromId(snd);
   ALint source = s->GetSoundSource();
   alSource3f(source, AL_POSITION, x, y, z);
}
void sound_3d_set_sound_velocity(int snd, float x, float y, float z) {
   Sound* s = GetSoundFromId(snd);
   ALint source = s->GetSoundSource();
   alSource3f(source, AL_VELOCITY, x, y, z);
}

Sound cones might be a little more complex since I haven't studied that part yet.
Also, GetSoundFromId is an hypothetical function. You'll have to replace it by whatever you really use in ENIGMA.
Same applies to GetSoundSource.
However, as you can see, the OpenAL-specific stuff is one line of code. In my own C#(obviously ENIGMA-unrelated, but still done with OpenAL) experiments, this works just great with Mono sounds(didn't test with Stereo).

The GM documentation mentions that "the listener is assumed to be at position (0,0,0)" and, I'm guessing, velocity (0, 0, 0), so the listener part will probably require no changes at all. All you have to do is find whatever code you use for the other audio functions, see how those functions get the sound "source" and the call the alSource3f. I don't even think you need any "alEnable" like you would in OpenGL.

132
OpenAL is a 3D audio library that is available across multiple platforms.
Although it supports 3D sound, it can also be used as a 2D audio library.

First, some concepts:

1. The context: Pretty much like OpenGL, you first need a context to work with. You have to create at least one context before having any sound. I'd recommend creating one for the default device(NULL) and then just keep using that one.
2. The listener: It doesn't matter what is playing around the world if there's nobody there to hear it. To have OpenAL working we have to initialize a listener.
3. The source: Similarly, it doesn't matter who is listening if there's nothing being played. Just like listeners *receive* sounds, sources *emit* sounds.
4. The buffer: So, we have a context, a listener and a source. The listener listens to sounds and the source plays those sounds. But exactly what sounds does the source play? The buffers store data to be played by sources. We will load our audio data to buffers, and then assign those buffers to sources.

So, let's get started:
Code: (c++) [Select]
#include <AL/al.h>
#include <AL/alc.h>
#include <cstdio>

int main() {
return 0;
}

Compile the code above using:
Code: [Select]
g++ -Wall -lopenal file.cpp -o program
Make sure you have everything you need installed, in particular the headers and libraries.

Now, our program doesn't do anything.
So we'll start by creating a context.

Code: (c++) [Select]
ALCdevice* device = alcOpenDevice(NULL);
ALCcontext* context = alcCreateContext(device, NULL);
alcMakeContextCurrent(context);

In the example above, we open the default audio device(NULL), and then create a context for that device.
Finally, we use that context.

Now, we're going to define our listener:

Code: (c++) [Select]
alListener3f(AL_POSITION, 0, 0, 0);
alListener3f(AL_VELOCITY, 0, 0, 0);
alListener3f(AL_ORIENTATION, 0, 0, -1);

Since we only want 2D sound, leave those values as they are. We're telling OpenAL where the listener is, where it is moving, etc.
We're going to put all listeners and sources in the origin with no speed.

Finally, we are going to have to load a audio file to a buffer and play it.
In this case, we're going to load the entire file to memory and play it all at once.

So first we are going to create a source:

Code: (c++) [Select]
ALuint source;
alGenSources(1, &source);

alSourcef(source, AL_PITCH, 1);
alSourcef(source, AL_GAIN, 1);
alSource3f(source, AL_POSITION, 0, 0, 0);
alSource3f(source, AL_VELOCITY, 0, 0, 0);
alSourcei(source, AL_LOOPING, AL_FALSE);

Note how you first allocate a source. We're also setting the properties of the source.
In particular, you might find the LOOPING value to be interesting. Set it to AL_TRUE to loop the audio instead of just playing it once.
AL_PITCH means how "fast" the sound is. 1 is the normal speed. Below 1 the sound will take longer to play. For instance, a 1 minute sound with pitch 0.5 will take 2 minutes, and only 30 seconds with a pitch of 2.
Do note that modifying the pitch of the sound will make the track sound differently.  Try it yourself and you'll see what I mean.

However, you won't listen to anything yet because the source has no buffer.
So, we must create a buffer first:

Code: (c++) [Select]
alGenBuffers(1, &buffer);

//TODO Load data to buffer

alSourcei(source, AL_BUFFER, buffer);

So our big problem is how to load data to the buffer.
I am going to be using the WAV sound format for now. Other formats may be added later.

The code I am using is the following. I will paste it once and then explain it:

Code: (c++) [Select]
FILE* f = fopen("audio.wav", "fb");
char xbuffer[5];
xbuffer[4] = '\0';
if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "RIFF") != 0)
throw "Not a WAV file";

file_read_int32_le(xbuffer, file);

if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "WAVE") != 0)
throw "Not a WAV file";

if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "fmt ") != 0)
throw "Invalid WAV file";

file_read_int32_le(xbuffer, file);
short audioFormat = file_read_int16_le(xbuffer, file);
short channels = file_read_int16_le(xbuffer, file);
int sampleRate = file_read_int32_le(xbuffer, file);
int byteRate = file_read_int32_le(xbuffer, file);
file_read_int16_le(xbuffer, file);
short bitsPerSample = file_read_int16_le(xbuffer, file);

if (audioFormat != 16) {
short extraParams = file_read_int16_le(xbuffer, file);
file_ignore_bytes(file, extraParams);
}

if (fread(xbuffer, sizeof(char), 4, file) != 4 || strcmp(xbuffer, "data") != 0)
throw "Invalid WAV file";

int dataChunkSize = file_read_int32_le(xbuffer, file);
unsigned char* bufferData = file_allocate_and_read_bytes(file, (size_t) dataChunkSize);

float duration = float(dataChunkSize) / byteRate;
alBufferData(buffer, GetFormatFromInfo(channels, bitsPerSample), bufferData, dataChunkSize, sampleRate);
free(bufferData);
fclose(f);

So now what is that big thing?
First we read the WAV header and extract the information from it, such as number of channels and rate.
Then, when we reach the section that contains the actual audio data, we load the entire thing to memory and load it to the buffer using alBufferData.

I have used multiple auxiliary functions:

1. file_read_int32_le(xbuffer, file) - I am using this function to read an integer of 32 bits from a file in little endian
2. file_read_int16_le(xbuffer, file) - This one is used to read 16 bits in little endian
3. file_ignore_bytes(file, nbytes) - Ignores N bytes from the file
4. file_allocate_and_read_bytes(file, nbytes) - Allocates a char* with N bytes and loads those bytes from the file
5. GetFormatFromInfo(channels, bitsPerSample) - Gets the AL format for the sound.

One possible (though incomplete) implementation of GetFormatFromInfo is:

Code: (c++) [Select]
static inline ALenum GetFormatFromInfo(short channels, short bitsPerSample) {
if (channels == 1)
return AL_FORMAT_MONO16;
return AL_FORMAT_STEREO16;
}

file_ignore_bytes can be implemented with a while+fgetc, or more efficiently in other ways.
file_allocate_and_read_bytes is essentially a malloc and a fread.
file_read_int32_le/file_read_int16_le is essentially a fread to the buffer, using count=4, and then using bit shifts and bitwise ors to format the data.

These functions are pretty easy to implement, so I'll leave them to you (the reader) as a C exercise.
You can also load WAV using ALUT, if you have it installed.

So now that we have this working, we're going to play the sound (and let it keep playing)

Code: (c++) [Select]
alSourcePlay(source);
fgetc(stdin);

This will keep playing the sound until the user presses enter in the console.

Finally, we'll do some cleanup:

Code: (c++) [Select]
alDeleteSources(1, &source);
alDeleteBuffers(1, &buffer);
alcDestroyContext(context);
alcCloseDevice(device);

Now, try it. It should be playing whatever file named "audio.wav" you have in your current path.

Some gems:
1. You can use alSourcePause(source) to pause the source, and then alSourcePlay(source) to start it again
2. You can use alSourceStop(source) to stop the source. Calling alSourcePlay(source) after that will start it over from the beginning
3. You can change your mind about looping at the middle of the stream. Want to loop the sound? No problem, just set AL_LOOPING to AL_TRUE.
4. You can also change the pitch while the sound is playing. For instance, if you have a game and your character dies, you could have the pitch go progressively lower to indicate a "Game Over".

Some limitations:
1. GetFormatFromInfo is incomplete. For instance, MONO8 and STEREO8 aren't properly supported.
2. The entire file is loaded to memory. Depending on the size of the file it might be a problem.
3. Loading the entire file at once might be slow. However, in my experience, even for large files, this isn't much of a problem for WAV files. Memory consumption, as indicated in 2, might be significantly worse.

Bonus Tricks:

1. How to find the current position of the sound being played?

Code: (c++) [Select]
int byteoffset;
alGetSourcei(source, AL_BYTE_OFFSET, &byteoffset);
return float(byteoffset) / byteRate;

Will return the number of seconds since the beginning of the sound file.

2. How to change the current position (e.g. skip some part of the sound)?

Code: (c++) [Select]
alSourcei(source, AL_BYTE_OFFSET, int(position * byteRate));

Both of these tricks only work when the entire file is in a single buffer.

EDIT 27 Apr 2012 - Fixed bug that could potentially corrupt memory. (Credits to Stephan Z.)

133
Proposals / Re: Object member functons
« on: January 09, 2011, 08:09:54 am »
I see.
What about:
Code: [Select]
(enigma::push_with(obj1), enigma::pop_with((enigma::push_with(obj2), enigma::pop_with(func()))))where pop_with(K) returns K.

If with_iter is already a stack, then I don't see how "obj1.func(obj2.func())" would fail. After all:
Code: [Select]
(enigma::with_iter(obj1), func((enigma::with_iter(obj2), func())))The inner func is called after obj2 is pushed to the stack, and before it is poped, so it is applied to obj2 alone, the top() of the stack(assuming a STL stack)
The outer func is called after obj1 and obj2 are pushed to the stack, but obj2 was already poped, so obj1 is the top() of the stack and therefore func() is applied to obj1 alone.

So I think it would work. That is, unless C++ sequence points are playing tricks on me.

134
Proposals / Re: Object member functons
« on: January 08, 2011, 01:36:01 pm »
Perhaps you should have with_iter use some sort of stack?

(enigma::push_with(obj1), func(enigma::push_with(obj2), func(), enigma::pop_with()), enigma::pop_with());

135
Proposals / Re: Object member functons
« on: January 07, 2011, 12:04:39 pm »
@polygone

Code: (EDL) [Select]
//a declared outside the with
with (obj)
   a = obj.f1();

Pages: « 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 »